import { db } from './db';
import { newId } from '@/lib/ids';
import {
  campaignSchema,
  characterSchema,
  encounterSchema,
  diceRollSchema,
  type Campaign,
  type CampaignDraft,
  type Character,
  type Encounter,
  type DiceRoll,
} from '@/lib/schemas';
import { getSystem } from '@/lib/rules';

function now(): string {
  return new Date().toISOString();
}

// ---------------- Campaigns ----------------
export const campaignsRepo = {
  async list(): Promise<Campaign[]> {
    const all = await db.campaigns.toArray();
    return all.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
  },

  get(id: string): Promise<Campaign | undefined> {
    return db.campaigns.get(id);
  },

  async create(draft: CampaignDraft): Promise<Campaign> {
    const ts = now();
    const campaign = campaignSchema.parse({
      ...draft,
      id: newId(),
      description: draft.description ?? '',
      createdAt: ts,
      updatedAt: ts,
    });
    await db.campaigns.add(campaign);
    return campaign;
  },

  async update(id: string, patch: Partial<Omit<Campaign, 'id' | 'createdAt'>>): Promise<void> {
    await db.campaigns.update(id, { ...patch, updatedAt: now() });
  },

  /** Delete a campaign and ALL its children atomically (real cascade). */
  async remove(id: string): Promise<void> {
    await db.transaction('rw', db.campaigns, db.characters, db.encounters, db.diceRolls, async () => {
      await db.characters.where('campaignId').equals(id).delete();
      await db.encounters.where('campaignId').equals(id).delete();
      await db.diceRolls.where('campaignId').equals(id).delete();
      await db.campaigns.delete(id);
    });
  },
};

// ---------------- Characters ----------------
export const charactersRepo = {
  listByCampaign(campaignId: string): Promise<Character[]> {
    return db.characters.where('campaignId').equals(campaignId).toArray();
  },

  get(id: string): Promise<Character | undefined> {
    return db.characters.get(id);
  },

  async create(
    campaignId: string,
    draft: Pick<Character, 'name' | 'kind' | 'ancestry' | 'className' | 'level'> & {
      system: Character['system'];
    },
  ): Promise<Character> {
    const ts = now();
    const sys = getSystem(draft.system);
    const character = characterSchema.parse({
      id: newId(),
      campaignId,
      system: draft.system,
      kind: draft.kind,
      name: draft.name,
      ancestry: draft.ancestry,
      className: draft.className,
      level: draft.level,
      abilities: sys.defaultAbilityScores(),
      hp: { current: 1, max: 1, temp: 0 },
      speed: 30,
      armorBonus: 0,
      skillRanks: {},
      saveRanks: {},
      perceptionRank: 'trained',
      notes: '',
      createdAt: ts,
      updatedAt: ts,
    });
    await db.characters.add(character);
    return character;
  },

  async update(id: string, patch: Partial<Omit<Character, 'id' | 'campaignId' | 'createdAt'>>): Promise<void> {
    await db.characters.update(id, { ...patch, updatedAt: now() });
  },

  /** Insert a fully-formed character (e.g. from an import). Validates on write. */
  async insert(character: Character): Promise<void> {
    await db.characters.add(characterSchema.parse(character));
  },

  async remove(id: string): Promise<void> {
    await db.characters.delete(id);
  },
};

// ---------------- Encounters ----------------
export const encountersRepo = {
  listByCampaign(campaignId: string): Promise<Encounter[]> {
    return db.encounters.where('campaignId').equals(campaignId).toArray();
  },

  get(id: string): Promise<Encounter | undefined> {
    return db.encounters.get(id);
  },

  async create(campaignId: string, name: string): Promise<Encounter> {
    const ts = now();
    const encounter = encounterSchema.parse({
      id: newId(),
      campaignId,
      name,
      status: 'planning',
      round: 0,
      turnIndex: 0,
      combatants: [],
      createdAt: ts,
      updatedAt: ts,
    });
    await db.encounters.add(encounter);
    return encounter;
  },

  /** Persist a full encounter (combat state lives as one document). */
  async save(encounter: Encounter): Promise<void> {
    const validated = encounterSchema.parse({ ...encounter, updatedAt: now() });
    await db.encounters.put(validated);
  },

  async remove(id: string): Promise<void> {
    await db.encounters.delete(id);
  },
};

// ---------------- Dice rolls ----------------
export const diceRepo = {
  async recent(campaignId: string | undefined, limit = 50): Promise<DiceRoll[]> {
    const coll = campaignId
      ? db.diceRolls.where('campaignId').equals(campaignId)
      : db.diceRolls.toCollection();
    const all = await coll.toArray();
    return all.sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, limit);
  },

  async add(roll: Omit<DiceRoll, 'id' | 'createdAt'>): Promise<DiceRoll> {
    const record = diceRollSchema.parse({ ...roll, id: newId(), createdAt: now() });
    await db.diceRolls.add(record);
    return record;
  },

  async clear(campaignId?: string): Promise<void> {
    if (campaignId) await db.diceRolls.where('campaignId').equals(campaignId).delete();
    else await db.diceRolls.clear();
  },
};
