import { beforeEach, describe, expect, it, vi } from 'vitest'

import { cleanupExpiredPostImportSessions,
	 clearPostImportSourceDraft,
	 loadPostImportSession,
	 loadPostImportSourceDraft,
	 savePostImportSession,
	 savePostImportSourceDraft } from '@/lib/postImportSession'
import { buildPostImportRow } from '@/test/postImportFactories'

describe ('post import storage', () => {
  beforeEach (() => {
    sessionStorage.clear ()
    vi.useRealTimers ()
  })

  it ('round-trips a valid session and source draft', () => {
    const row = buildPostImportRow ({
      attributes: { duration: '2.5' },
      recoverable: true,
      importStatus: 'pending',
      validationErrors: { title: ['invalid'] } })
    const skipped = buildPostImportRow ({
      importStatus: 'skipped',
      skipReason: 'existing',
      existingPostId: 10 })
    const manual = buildPostImportRow ({
      sourceRow: 3,
      skipReason: 'manual' })

    expect (savePostImportSession ({
      source: skipped.url,
      rows: [row, skipped, manual],
      repairMode: 'all' })).toBe (true)
    expect (loadPostImportSession ()).toMatchObject ({
      version: 2,
      source: skipped.url,
      rows: [{
	attributes: { duration: '2.5' },
	recoverable: true,
	importStatus: 'pending' },
	      {
	importStatus: 'skipped',
	skipReason: 'existing',
	existingPostId: 10 },
	      {
	skipReason: 'manual' }] })

    expect (savePostImportSourceDraft ('https://example.com')).toBe (true)
    expect (loadPostImportSourceDraft ()).toEqual ({ source: 'https://example.com' })
    clearPostImportSourceDraft ()
    expect (loadPostImportSourceDraft ()).toEqual ({ source: '' })
  })

  it ('rejects inconsistent post IDs and terminal statuses', () => {
    const session = {
      version: 2,
      savedAt: new Date ().toISOString (),
      source: '',
      repairMode: 'all',
      rows: [buildPostImportRow ()] }
    const invalidRows = [
      { ...session.rows[0], skipReason: 'existing', existingPostId: undefined },
      { ...session.rows[0], existingPostId: 2, skipReason: undefined },
      { ...session.rows[0], importStatus: 'created', createdPostId: undefined },
      { ...session.rows[0], importStatus: 'failed', createdPostId: 3 },
      { ...session.rows[0], importStatus: 'created', recoverable: true },
      { ...session.rows[0], importStatus: 'skipped', recoverable: true },
      { ...session.rows[0], recoverable: true }]

    for (const row of invalidRows)
      {
	sessionStorage.setItem ('post-import-session:current', JSON.stringify ({
	  ...session,
	  rows: [row] }))
	expect (loadPostImportSession ()).toBeNull ()
	sessionStorage.clear ()
      }
  })

  it ('rejects invalid attribute, provenance, tag-source, and snapshot data', () => {
    const row = buildPostImportRow ()
    const invalidRows = [
      { ...row, attributes: { title: [] } },
      { ...row, attributes: { unknown: 'value' } },
      { ...row, provenance: { title: 'mapped' } },
      { ...row, tagSources: { mapped: 'tag' } },
      { ...row, resetSnapshot: { ...row.resetSnapshot,
	fieldWarnings: { title: 'warning' } } }]

    invalidRows.forEach (invalidRow => {
      sessionStorage.setItem ('post-import-session:current', JSON.stringify ({
	version: 2,
	savedAt: new Date ().toISOString (),
	source: '',
	rows: [invalidRow],
	repairMode: 'all' }))
      expect (loadPostImportSession ()).toBeNull ()
      sessionStorage.clear ()
    })
  })

  it ('keeps only the current fixed-key session and removes legacy prefixed entries', () => {
    const current = {
      version: 2,
      savedAt: new Date ().toISOString (),
      source: '',
      rows: [buildPostImportRow ()],
      repairMode: 'all' }
    const expired = {
      ...current,
      savedAt: new Date (Date.now () - 25 * 60 * 60 * 1000).toISOString () }
    sessionStorage.setItem ('post-import-session:current', JSON.stringify (current))
    sessionStorage.setItem ('post-import-session:expired', JSON.stringify (expired))
    sessionStorage.setItem ('post-import-session:malformed', '{')

    cleanupExpiredPostImportSessions ()

    expect (sessionStorage.getItem ('post-import-session:current')).not.toBeNull ()
    expect (sessionStorage.getItem ('post-import-session:expired')).toBeNull ()
    expect (sessionStorage.getItem ('post-import-session:malformed')).toBeNull ()
  })

  it ('reports storage access failures without throwing', () => {
    const onError = vi.fn ()
    vi.spyOn (Storage.prototype, 'setItem').mockImplementation (() => {
      throw new DOMException ('quota')
    })

    expect (savePostImportSourceDraft ('source', onError)).toBe (false)
    expect (onError).toHaveBeenCalledWith ('ブラウザへ保存できませんでした．')
  })
})
