import Dexie, { type Table } from 'dexie'
import type { SaveData } from '../types'

interface SaveRecord {
  id?: number
  slot: number
  timestamp: number
  currentScene: string
  variables: string
  flags: string
  history: string
  thumbnail?: string
}

interface UnlockRecord {
  chapterId: string
}

interface WatchedRecord {
  sceneId: string
  timestamp: number
}

interface AchievementRecord {
  achievementId: string
}

interface VisitedRecord {
  sceneId: string
}

class SaveDB extends Dexie {
  saves!: Table<SaveRecord, number>
  unlocks!: Table<UnlockRecord, string>
  watched!: Table<WatchedRecord, string>
  achievements!: Table<AchievementRecord, string>
  visited!: Table<VisitedRecord, string>

  constructor() {
    super('MovieGameSaves')
    this.version(6).stores({
      saves: '++id, slot',
      unlocks: 'chapterId',
      watched: 'sceneId',
      achievements: 'achievementId',
      visited: 'sceneId',
    })
  }
}

const db = new SaveDB()

export class SaveSystem {
  async save(slot: number, data: Omit<SaveData, 'slot'>): Promise<void> {
    const record: SaveRecord = {
      slot,
      timestamp: data.timestamp || Date.now(),
      currentScene: data.currentScene,
      variables: JSON.stringify(data.variables),
      flags: JSON.stringify(data.flags),
      history: JSON.stringify(data.history),
      thumbnail: data.thumbnail,
    }

    const existing = await db.saves.where('slot').equals(slot).first()
    if (existing) {
      await db.saves.update(existing.id!, record)
    } else {
      await db.saves.add(record)
    }
  }

  async load(slot: number): Promise<SaveData | null> {
    const record = await db.saves.where('slot').equals(slot).first()
    if (!record) return null

    return {
      slot: record.slot,
      timestamp: record.timestamp,
      currentScene: record.currentScene,
      variables: JSON.parse(record.variables),
      flags: JSON.parse(record.flags),
      history: JSON.parse(record.history),
      thumbnail: record.thumbnail,
    }
  }

  async listSlots(): Promise<{ slot: number; timestamp: number; sceneLabel: string; thumbnail?: string }[]> {
    const records = await db.saves.orderBy('slot').toArray()
    return records.map((r) => ({
      slot: r.slot,
      timestamp: r.timestamp,
      sceneLabel: r.currentScene,
      thumbnail: r.thumbnail,
    }))
  }

  async delete(slot: number): Promise<void> {
    await db.saves.where('slot').equals(slot).delete()
  }

  async unlockChapter(chapterId: string) {
    const exists = await db.unlocks.get(chapterId)
    if (!exists) {
      await db.unlocks.put({ chapterId })
    }
  }

  async isChapterUnlocked(chapterId: string): Promise<boolean> {
    const record = await db.unlocks.get(chapterId)
    return !!record
  }

  async getUnlockedChapters(): Promise<string[]> {
    const records = await db.unlocks.toArray()
    return records.map((r) => r.chapterId)
  }

  async markWatched(sceneId: string) {
    const exists = await db.watched.get(sceneId)
    if (!exists) {
      await db.watched.put({ sceneId, timestamp: Date.now() })
    }
  }

  async isWatched(sceneId: string): Promise<boolean> {
    const record = await db.watched.get(sceneId)
    return !!record
  }

  async getWatchedSceneIds(): Promise<string[]> {
    const records = await db.watched.toArray()
    return records.map((r) => r.sceneId)
  }

  async unlockAchievement(id: string) {
    const exists = await db.achievements.get(id)
    if (!exists) {
      await db.achievements.put({ achievementId: id })
    }
  }

  async getUnlockedAchievements(): Promise<string[]> {
    const records = await db.achievements.toArray()
    return records.map((r) => r.achievementId)
  }

  async markVisited(sceneId: string) {
    const exists = await db.visited.get(sceneId)
    if (!exists) {
      await db.visited.put({ sceneId })
    }
  }

  async getVisitedSceneIds(): Promise<string[]> {
    const records = await db.visited.toArray()
    return records.map((r) => r.sceneId)
  }
}
