import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useSeriesManageStore, moveItem } from '../src/stores/seriesManage.js'

function stubFetch(handler) {
  globalThis.fetch = vi.fn(async (url, init) => {
    const { status, body } = handler(url, init)
    return {
      ok: status >= 200 && status < 300,
      status, statusText: String(status),
      text: async () => (body == null ? '' : JSON.stringify(body))
    }
  })
}

// FC-6.x: a flat page run + cosmetic chapter dividers.
const SERIES_BODY = {
  series: { id: 7, name: 'V' },
  pages: [{ image_id: 1, page_number: 1, thumbnail_url: 't', source_post: null }],
  dividers: [
    { id: 1, anchor_image_id: 1, page_number: 1, title: null, stated_part: null }
  ],
  part_gaps: []
}

describe('seriesManage', () => {
  beforeEach(() => setActivePinia(createPinia()))
  afterEach(() => vi.restoreAllMocks())

  it('moveItem reorders immutably', () => {
    expect(moveItem([1, 2, 3, 4], 3, 0)).toEqual([4, 1, 2, 3])
    expect(moveItem([1, 2, 3], 0, 2)).toEqual([2, 3, 1])
  })

  it('load fetches the flat pages + dividers + series', async () => {
    const s = useSeriesManageStore()
    stubFetch(() => ({ status: 200, body: SERIES_BODY }))
    await s.load(7)
    expect(s.series).toEqual({ id: 7, name: 'V' })
    expect(s.pages.map(p => p.image_id)).toEqual([1])
    expect(s.dividers.map(d => d.id)).toEqual([1])
    expect(s.pageCount).toBe(1)
  })

  it('setPageNumber posts the image + number to /pages/number', async () => {
    const s = useSeriesManageStore()
    s.tagId = 7
    const calls = []
    stubFetch((url, init) => {
      calls.push({ url, body: init.body ? JSON.parse(init.body) : null })
      if (url.includes('/pages/number')) return { status: 200, body: { ok: true } }
      return { status: 200, body: SERIES_BODY }
    })
    await s.setPageNumber(42, 5)
    const r = calls.find(c => c.url.includes('/pages/number'))
    expect(r.url).toContain('/api/series/7/pages/number')
    expect(r.body).toEqual({ image_id: 42, page_number: 5 })
  })

  it('gapAfter looks up a gap by its after_image_id', async () => {
    const s = useSeriesManageStore()
    stubFetch(() => ({
      status: 200,
      body: { ...SERIES_BODY, gaps: [{ after_image_id: 1, before_image_id: 9, from: 2, to: 3 }] }
    }))
    await s.load(7)
    expect(s.gapAfter(1)).toEqual({ after_image_id: 1, before_image_id: 9, from: 2, to: 3 })
    expect(s.gapAfter(99)).toBeNull()
  })

  it('createDivider posts the anchor image + labels', async () => {
    const s = useSeriesManageStore()
    s.tagId = 7
    const calls = []
    stubFetch((url, init) => {
      calls.push({ url, method: init.method, body: init.body ? JSON.parse(init.body) : null })
      if (init.method === 'POST' && url.endsWith('/chapters'))
        return { status: 200, body: { id: 2, anchor_image_id: 5 } }
      return { status: 200, body: SERIES_BODY }
    })
    await s.createDivider(5, { title: 'X', statedPart: 2 })
    const c = calls.find(x => x.method === 'POST' && x.url.endsWith('/chapters'))
    expect(c.url).toContain('/api/series/7/chapters')
    expect(c.body).toEqual({ anchor_image_id: 5, title: 'X', stated_part: 2 })
  })

  it('setDividerPart patches stated_part on the divider', async () => {
    const s = useSeriesManageStore()
    s.tagId = 7
    const calls = []
    stubFetch((url, init) => {
      calls.push({ url, method: init.method, body: init.body ? JSON.parse(init.body) : null })
      if (init.method === 'PATCH') return { status: 200, body: { ok: true } }
      return { status: 200, body: SERIES_BODY }
    })
    await s.setDividerPart(1, 2)
    const p = calls.find(c => c.method === 'PATCH')
    expect(p.url).toContain('/api/series/7/chapters/1')
    expect(p.body).toEqual({ stated_part: 2 })
  })

  it('load surfaces part_gaps and partGapAfter looks them up', async () => {
    const s = useSeriesManageStore()
    stubFetch(() => ({
      status: 200,
      body: { ...SERIES_BODY, part_gaps: [{ after_divider_id: 1, start: 2, end: 2 }] }
    }))
    await s.load(7)
    expect(s.partGaps).toHaveLength(1)
    expect(s.partGapAfter(1)).toEqual({ after_divider_id: 1, start: 2, end: 2 })
    expect(s.partGapAfter(99)).toBeNull()
  })

  it('dividerAt finds a divider by its anchor image', async () => {
    const s = useSeriesManageStore()
    stubFetch(() => ({ status: 200, body: SERIES_BODY }))
    await s.load(7)
    expect(s.dividerAt(1)?.id).toBe(1)
    expect(s.dividerAt(99)).toBeNull()
  })

  it('addSelected posts the selection (no chapter) then clears', async () => {
    const s = useSeriesManageStore()
    s.tagId = 7
    s.pickerSelection = [9, 10]
    const calls = []
    stubFetch((url, init) => {
      calls.push({ url, body: init.body ? JSON.parse(init.body) : null })
      if (url.endsWith('/pages') && init.method === 'POST')
        return { status: 200, body: { added_count: 2 } }
      return { status: 200, body: SERIES_BODY }
    })
    await s.addSelected()
    const add = calls.find(c => c.url.endsWith('/api/series/7/pages'))
    expect(add.body).toEqual({ image_ids: [9, 10] })
    expect(s.pickerSelection).toEqual([])
  })
})
