import 'dotenv/config';
import PocketBase, { type CollectionModel } from 'pocketbase';

// ---------------------------------------------------------------------------
// Env validation
// ---------------------------------------------------------------------------

const POCKETBASE_URL = process.env['POCKETBASE_URL'] ?? '';
const POCKETBASE_ADMIN_EMAIL = process.env['POCKETBASE_ADMIN_EMAIL'] ?? '';
const POCKETBASE_ADMIN_PASSWORD = process.env['POCKETBASE_ADMIN_PASSWORD'] ?? '';

if (!POCKETBASE_URL || !POCKETBASE_ADMIN_EMAIL || !POCKETBASE_ADMIN_PASSWORD) {
  console.error('Missing env vars: POCKETBASE_URL, POCKETBASE_ADMIN_EMAIL, POCKETBASE_ADMIN_PASSWORD');
  process.exit(1);
}

// ---------------------------------------------------------------------------
// Field types
// ---------------------------------------------------------------------------

interface FieldDef {
  name: string;
  type: string;
  required: boolean;
  options: Record<string, unknown>;
}

// ---------------------------------------------------------------------------
// Field helpers
// ---------------------------------------------------------------------------

const field = {
  text: (name: string, required = false): FieldDef => ({
    name, type: 'text', required,
    options: { min: null, max: null, pattern: '' },
  }),

  number: (name: string, required = false): FieldDef => ({
    name, type: 'number', required,
    options: { min: null, max: null, noDecimal: false },
  }),

  select: (name: string, values: string[], required = false, maxSelect = 1): FieldDef => ({
    name, type: 'select', required,
    options: { maxSelect, values },
  }),

  json: (name: string): FieldDef => ({
    name, type: 'json', required: false, options: { maxSize: 2097152 },
  }),

  date: (name: string): FieldDef => ({
    name, type: 'date', required: false, options: { min: '', max: '' },
  }),

  editor: (name: string): FieldDef => ({
    name, type: 'editor', required: false, options: {},
  }),

  file: (name: string, mimeTypes: string[] = [], maxSelect = 1): FieldDef => ({
    name, type: 'file', required: false,
    options: { maxSelect, maxSize: 52428800, mimeTypes, thumbs: [], protected: false },
  }),

  relation: (name: string, collectionId: string, maxSelect: number | null = 1, required = false): FieldDef => ({
    name, type: 'relation', required,
    options: { collectionId, cascadeDelete: false, minSelect: null, maxSelect, displayFields: null },
  }),
};

// ---------------------------------------------------------------------------
// Badge seed data (from data-model.md + handover.md)
// ---------------------------------------------------------------------------

const BADGES = [
  // Bronze — beginner milestones
  { key: 'first_commit',    tier: 'bronze',    label: 'First Commit',    description: 'Complete your first topic',                          icon: '🔰' },
  { key: 'week_shipped',    tier: 'bronze',    label: 'Week Shipped',    description: 'Complete your first full week',                      icon: '📦' },
  { key: 'streak_3',        tier: 'bronze',    label: '3-Week Streak',   description: 'Maintain a 3-week learning streak',                  icon: '🔥' },
  { key: 'quiz_taker',      tier: 'bronze',    label: 'Quiz Taker',      description: 'Complete your first scenario quiz',                  icon: '❓' },
  { key: 'flashcard_fan',   tier: 'bronze',    label: 'Flashcard Fan',   description: 'Complete your first flashcard set',                  icon: '🃏' },
  { key: 'reached_junior',  tier: 'bronze',    label: 'Junior Dev',      description: 'Reach Junior level',                                 icon: '👶' },
  // Silver — intermediate
  { key: 'streak_13',       tier: 'silver',    label: '13-Week Streak',  description: 'Maintain a 13-week learning streak',                 icon: '💥' },
  { key: 'half_cycle',      tier: 'silver',    label: 'Half Cycle',      description: 'Complete week 13 — halfway through the cycle',       icon: '🔄' },
  { key: 'case_student',    tier: 'silver',    label: 'Case Student',    description: 'Complete 5 case studies',                            icon: '📋' },
  { key: 'reached_medior',  tier: 'silver',    label: 'Medior Dev',      description: 'Reach Medior level',                                 icon: '💼' },
  { key: 'reached_senior',  tier: 'silver',    label: 'Senior Dev',      description: 'Reach Senior level',                                 icon: '🏅' },
  // Gold — advanced
  { key: 'full_cycle',      tier: 'gold',      label: 'Full Cycle',      description: 'Complete a full 26-week cycle',                      icon: '🏆' },
  { key: 'type_explorer',   tier: 'gold',      label: 'Type Explorer',   description: 'Use all 10 micro learning types at least once',      icon: '🗺️' },
  { key: 'reached_staff',   tier: 'gold',      label: 'Staff Eng',       description: 'Reach Staff level',                                  icon: '⭐' },
  // Legendary — exceptional
  { key: 'streak_26',       tier: 'legendary', label: 'Unbroken',        description: 'Complete all 26 weeks without breaking a streak',    icon: '⚡' },
  { key: 'second_cycle',    tier: 'legendary', label: 'Second Cycle',    description: 'Complete two full cycles',                           icon: '🌀' },
  { key: 'reached_principal', tier: 'legendary', label: 'Principal',     description: 'Reach Principal level — maximum rank',               icon: '👑' },
  // Content — domain mastery
  { key: 'governance_nerd',   tier: 'content', label: 'Governance Nerd',   description: 'Complete all published governance topics',         icon: '⚖️' },
  { key: 'process_architect', tier: 'content', label: 'Process Architect', description: 'Complete all published process topics',            icon: '🏗️' },
  { key: 'deep_reader',       tier: 'content', label: 'Deep Reader',       description: 'Complete 50 or more unique topics',                icon: '📚' },
] as const;

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function requireId(ids: Map<string, string>, name: string): string {
  const id = ids.get(name);
  if (id === undefined) throw new Error(`Collection ID not found: ${name}`);
  return id;
}

// ---------------------------------------------------------------------------
// Migration
// ---------------------------------------------------------------------------

async function run(): Promise<void> {
  console.log('Connecting to PocketBase...');
  const pb = new PocketBase(POCKETBASE_URL);
  await pb.admins.authWithPassword(POCKETBASE_ADMIN_EMAIL, POCKETBASE_ADMIN_PASSWORD);
  console.log('Authenticated.\n');

  // Snapshot of existing collections before we begin
  const existingCollections = await pb.collections.getFullList();
  const ids = new Map<string, string>();
  for (const col of existingCollections) {
    ids.set(col.name, col.id);
  }
  const existingNames = new Set(ids.keys());

  // Create a collection only if it doesn't already exist
  async function ensureCollection(params: { name: string; type: string; schema: FieldDef[] }): Promise<void> {
    if (ids.has(params.name)) {
      console.log(`  skip   ${params.name}`);
      return;
    }
    const created = await pb.collections.create(params as Record<string, unknown>);
    ids.set(created.name, created.id);
    console.log(`  create ${created.name}`);
  }

  // ---------------------------------------------------------------------------
  // Extend users (auth collection — already exists in PocketBase)
  // ---------------------------------------------------------------------------
  console.log('users:');
  const usersCol = await pb.collections.getFirstListItem<CollectionModel>('name="users"');
  ids.set('users', usersCol.id);

  const existingFieldNames = new Set(usersCol.schema.map((s: { name: string }) => s.name));
  const fieldsToAdd: FieldDef[] = [];
  if (!existingFieldNames.has('role')) fieldsToAdd.push(field.select('role', ['admin', 'employee'], true));
  if (!existingFieldNames.has('display_name')) fieldsToAdd.push(field.text('display_name'));

  if (fieldsToAdd.length > 0) {
    const updateBody: Record<string, unknown> = {
      schema: [...usersCol.schema, ...fieldsToAdd],
    };
    await pb.collections.update(usersCol.id, updateBody);
    console.log(`  extended with: ${fieldsToAdd.map((f: FieldDef) => f.name).join(', ')}`);
  } else {
    console.log('  skip   users (already extended)');
  }

  // ---------------------------------------------------------------------------
  // source_documents
  // ---------------------------------------------------------------------------
  console.log('\ncollections:');
  await ensureCollection({
    name: 'source_documents',
    type: 'base',
    schema: [
      field.text('filename', true),
      field.file('file', ['application/pdf', 'text/markdown', 'text/x-markdown', 'text/plain']),
      field.select('format', ['pdf', 'md', 'txt'], true),
      field.select('status', ['processing', 'processed', 'failed'], true),
      field.date('ingested_at'),
      field.number('chunk_count'),
      field.relation('created_by', requireId(ids, 'users')),
    ],
  });

  // ---------------------------------------------------------------------------
  // themes
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'themes',
    type: 'base',
    schema: [
      field.text('title', true),
      field.text('description'),
      field.select('status', ['draft', 'published'], true),
      field.relation('source_documents', requireId(ids, 'source_documents'), null),
      field.relation('approved_by', requireId(ids, 'users')),
      field.date('approved_at'),
    ],
  });

  // ---------------------------------------------------------------------------
  // topics — create without self-referential fields first
  // ---------------------------------------------------------------------------
  const topicsBaseSchema: FieldDef[] = [
    field.relation('theme', requireId(ids, 'themes'), 1, true),
    field.text('title', true),
    field.editor('body'),
    field.select('difficulty', ['introductory', 'intermediate', 'advanced'], true),
    field.number('complexity_weight'),
    field.select('status', ['draft', 'published'], true),
    field.json('key_terms'),
    field.json('qdrant_chunk_ids'),
  ];
  const topicsIsNew = !existingNames.has('topics');
  await ensureCollection({ name: 'topics', type: 'base', schema: topicsBaseSchema });

  // ---------------------------------------------------------------------------
  // micro_learnings
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'micro_learnings',
    type: 'base',
    schema: [
      field.relation('topic', requireId(ids, 'topics'), 1, true),
      field.select('type', [
        'concept_explainer', 'scenario_quiz', 'misconceptions', 'how_to',
        'comparison_card', 'reflection_prompt', 'flashcard_set', 'case_study',
        'glossary_anchor', 'myth_vs_evidence',
      ], true),
      field.json('content'),
      field.select('status', ['queued', 'generated', 'published', 'rejected'], true),
      field.text('generation_model'),
      field.date('generated_at'),
      field.date('published_at'),
    ],
  });

  // ---------------------------------------------------------------------------
  // curriculum_versions
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'curriculum_versions',
    type: 'base',
    schema: [
      field.number('version', true),
      field.select('status', ['draft', 'active', 'superseded'], true),
      field.date('generated_at'),
      field.relation('approved_by', requireId(ids, 'users')),
      field.date('approved_at'),
      field.text('generation_notes'),
    ],
  });

  // ---------------------------------------------------------------------------
  // curriculum_weeks
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'curriculum_weeks',
    type: 'base',
    schema: [
      field.relation('curriculum_version', requireId(ids, 'curriculum_versions'), 1, true),
      field.number('week_number', true),
      field.relation('theme', requireId(ids, 'themes'), 1, true),
      field.relation('topics', requireId(ids, 'topics'), null),
      field.json('topic_order'),
      field.number('estimated_duration_minutes'),
      field.text('admin_notes'),
    ],
  });

  // ---------------------------------------------------------------------------
  // employee_curriculum_state
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'employee_curriculum_state',
    type: 'base',
    schema: [
      field.relation('user', requireId(ids, 'users'), 1, true),
      field.number('current_cycle', true),
      field.number('current_week', true),
      field.date('start_date'),
      field.relation('active_version', requireId(ids, 'curriculum_versions')),
    ],
  });

  // ---------------------------------------------------------------------------
  // session_completions
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'session_completions',
    type: 'base',
    schema: [
      field.relation('user', requireId(ids, 'users'), 1, true),
      field.relation('topic', requireId(ids, 'topics'), 1, true),
      field.relation('micro_learning', requireId(ids, 'micro_learnings'), 1, true),
      field.number('week_number', true),
      field.number('cycle', true),
      field.date('completed_at'),
    ],
  });

  // ---------------------------------------------------------------------------
  // gamification_profiles
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'gamification_profiles',
    type: 'base',
    schema: [
      field.relation('user', requireId(ids, 'users'), 1, true),
      field.number('total_commits'),
      field.select('current_level', ['intern', 'junior', 'medior', 'senior', 'staff', 'principal']),
      field.number('current_streak_weeks'),
      field.number('longest_streak_weeks'),
      field.json('types_used'),
      field.number('last_active_week'),
    ],
  });

  // ---------------------------------------------------------------------------
  // badges
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'badges',
    type: 'base',
    schema: [
      field.text('key', true),
      field.select('tier', ['bronze', 'silver', 'gold', 'legendary', 'content'], true),
      field.text('label', true),
      field.text('description'),
      field.text('icon'),
    ],
  });

  // ---------------------------------------------------------------------------
  // employee_badges
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'employee_badges',
    type: 'base',
    schema: [
      field.relation('user', requireId(ids, 'users'), 1, true),
      field.relation('badge', requireId(ids, 'badges'), 1, true),
      field.date('earned_at'),
      field.number('cycle'),
    ],
  });

  // ---------------------------------------------------------------------------
  // milestone_cards
  // ---------------------------------------------------------------------------
  await ensureCollection({
    name: 'milestone_cards',
    type: 'base',
    schema: [
      field.relation('user', requireId(ids, 'users'), 1, true),
      field.number('cycle', true),
      field.number('week', true),
      field.number('total_commits'),
      field.number('streak_weeks'),
      field.json('badge_keys'),
    ],
  });

  // ---------------------------------------------------------------------------
  // Add self-referential relations to topics (requires topics ID to exist first)
  // ---------------------------------------------------------------------------
  if (topicsIsNew) {
    console.log('\ntopics self-refs:');
    const topicsId = requireId(ids, 'topics');
    const updateBody: Record<string, unknown> = {
      schema: [
        ...topicsBaseSchema,
        field.relation('related_topics', topicsId, null),
        field.relation('prerequisite_topics', topicsId, null),
        field.relation('contrast_topics', topicsId, null),
      ],
    };
    await pb.collections.update(topicsId, updateBody);
    console.log('  updated topics with related_topics, prerequisite_topics, contrast_topics');
  }

  // ---------------------------------------------------------------------------
  // Seed badges
  // ---------------------------------------------------------------------------
  console.log('\nbadges:');
  const existingBadges = await pb.collection('badges').getFullList<{ key: string }>();
  const existingBadgeKeys = new Set(existingBadges.map(b => b.key));

  for (const badge of BADGES) {
    if (existingBadgeKeys.has(badge.key)) {
      console.log(`  skip   ${badge.key}`);
      continue;
    }
    await pb.collection('badges').create(badge);
    console.log(`  seed   ${badge.key}`);
  }

  console.log('\nDone.');
}

run().catch((err: unknown) => {
  console.error('Migration failed:', err);
  process.exit(1);
});
