#!/usr/bin/env tsx
/**
 * Migration Script: SQLite → Supabase
 *
 * This script migrates all data from the local SQLite database to Supabase.
 * Run with: npx tsx scripts/migrate-to-supabase.ts
 */

import { createClient } from '@supabase/supabase-js';
import Database from 'better-sqlite3';
import { join } from 'path';
import { config } from 'dotenv';

// Load environment variables
config({ path: '.env.local' });

// Validate environment variables
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL;
const SUPABASE_SERVICE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY;

if (!SUPABASE_URL || !SUPABASE_SERVICE_KEY) {
  console.error('❌ Missing environment variables!');
  console.error('Make sure you have created .env.local with:');
  console.error('  - NEXT_PUBLIC_SUPABASE_URL');
  console.error('  - SUPABASE_SERVICE_ROLE_KEY');
  process.exit(1);
}

// Initialize clients
const sqliteDb = new Database(join(process.cwd(), 'data', 'tasks.db'));
const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_KEY, {
  auth: { autoRefreshToken: false, persistSession: false }
});

// Helper to convert SQLite ID to UUID (deterministic)
function generateUUIDFromString(str: string): string {
  // Create a deterministic UUID v5-like string from the input
  // This ensures the same SQLite ID always maps to the same UUID
  const hash = str.split('').reduce((acc, char) => {
    return ((acc << 5) - acc) + char.charCodeAt(0) | 0;
  }, 0);

  const hex = Math.abs(hash).toString(16).padStart(32, '0');
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
}

// Track ID mappings
const userIdMap = new Map<string, string>();
const projectIdMap = new Map<string, string>();
const sprintIdMap = new Map<string, string>();

async function migrateUsers() {
  console.log('📦 Migrating users...');

  const users = sqliteDb.prepare('SELECT * FROM users').all() as Array<{
    id: string;
    name: string;
    email: string;
    avatarUrl: string | null;
    passwordHash: string;
    createdAt: string;
  }>;

  let migrated = 0;
  let skipped = 0;

  for (const user of users) {
    const uuid = generateUUIDFromString(user.id);
    userIdMap.set(user.id, uuid);

    const { error } = await supabase
      .from('users')
      .upsert({
        id: uuid,
        legacy_id: user.id,
        name: user.name,
        email: user.email.toLowerCase().trim(),
        avatar_url: user.avatarUrl,
        password_hash: user.passwordHash,
        created_at: user.createdAt,
      }, { onConflict: 'email' });

    if (error) {
      console.error(`  ❌ Failed to migrate user ${user.email}:`, error.message);
    } else {
      migrated++;
      console.log(`  ✓ ${user.email}`);
    }
  }

  console.log(`  ✅ Migrated ${migrated} users (${skipped} skipped)\n`);
  return migrated;
}

async function migrateSessions() {
  console.log('📦 Migrating sessions...');

  const sessions = sqliteDb.prepare('SELECT * FROM sessions').all() as Array<{
    id: string;
    userId: string;
    tokenHash: string;
    createdAt: string;
    expiresAt: string;
  }>;

  let migrated = 0;

  for (const session of sessions) {
    const userUuid = userIdMap.get(session.userId);
    if (!userUuid) {
      console.log(`  ⚠️ Skipping session for unknown user: ${session.userId}`);
      continue;
    }

    const { error } = await supabase
      .from('sessions')
      .upsert({
        id: generateUUIDFromString(session.id),
        user_id: userUuid,
        token_hash: session.tokenHash,
        created_at: session.createdAt,
        expires_at: session.expiresAt,
      }, { onConflict: 'token_hash' });

    if (error) {
      console.error(`  ❌ Failed to migrate session:`, error.message);
    } else {
      migrated++;
    }
  }

  console.log(`  ✅ Migrated ${migrated} sessions\n`);
  return migrated;
}

async function migratePasswordResetTokens() {
  console.log('📦 Migrating password reset tokens...');

  const tokens = sqliteDb.prepare('SELECT * FROM password_reset_tokens').all() as Array<{
    id: string;
    userId: string;
    tokenHash: string;
    expiresAt: string;
    createdAt: string;
    used: number;
  }>;

  let migrated = 0;

  for (const token of tokens) {
    const userUuid = userIdMap.get(token.userId);
    if (!userUuid) {
      console.log(`  ⚠️ Skipping token for unknown user: ${token.userId}`);
      continue;
    }

    const { error } = await supabase
      .from('password_reset_tokens')
      .upsert({
        id: generateUUIDFromString(token.id),
        user_id: userUuid,
        token_hash: token.tokenHash,
        expires_at: token.expiresAt,
        created_at: token.createdAt,
        used: token.used === 1,
      }, { onConflict: 'token_hash' });

    if (error) {
      console.error(`  ❌ Failed to migrate token:`, error.message);
    } else {
      migrated++;
    }
  }

  console.log(`  ✅ Migrated ${migrated} password reset tokens\n`);
  return migrated;
}

async function migrateProjects() {
  console.log('📦 Migrating projects...');

  const projects = sqliteDb.prepare('SELECT * FROM projects').all() as Array<{
    id: string;
    name: string;
    description: string | null;
    color: string;
    createdAt: string;
  }>;

  let migrated = 0;

  for (const project of projects) {
    const uuid = generateUUIDFromString(project.id);
    projectIdMap.set(project.id, uuid);

    const { error } = await supabase
      .from('projects')
      .upsert({
        id: uuid,
        legacy_id: project.id,
        name: project.name,
        description: project.description,
        color: project.color,
        created_at: project.createdAt,
      }, { onConflict: 'legacy_id' });

    if (error) {
      console.error(`  ❌ Failed to migrate project ${project.name}:`, error.message);
    } else {
      migrated++;
      console.log(`  ✓ ${project.name}`);
    }
  }

  console.log(`  ✅ Migrated ${migrated} projects\n`);
  return migrated;
}

async function migrateSprints() {
  console.log('📦 Migrating sprints...');

  const sprints = sqliteDb.prepare('SELECT * FROM sprints').all() as Array<{
    id: string;
    name: string;
    goal: string | null;
    startDate: string;
    endDate: string;
    status: string;
    projectId: string;
    createdAt: string;
  }>;

  let migrated = 0;

  for (const sprint of sprints) {
    const uuid = generateUUIDFromString(sprint.id);
    sprintIdMap.set(sprint.id, uuid);

    const projectUuid = projectIdMap.get(sprint.projectId);
    if (!projectUuid) {
      console.log(`  ⚠️ Skipping sprint ${sprint.name} - unknown project: ${sprint.projectId}`);
      continue;
    }

    const { error } = await supabase
      .from('sprints')
      .upsert({
        id: uuid,
        legacy_id: sprint.id,
        name: sprint.name,
        goal: sprint.goal,
        start_date: sprint.startDate,
        end_date: sprint.endDate,
        status: sprint.status,
        project_id: projectUuid,
        created_at: sprint.createdAt,
      }, { onConflict: 'legacy_id' });

    if (error) {
      console.error(`  ❌ Failed to migrate sprint ${sprint.name}:`, error.message);
    } else {
      migrated++;
      console.log(`  ✓ ${sprint.name}`);
    }
  }

  console.log(`  ✅ Migrated ${migrated} sprints\n`);
  return migrated;
}

async function migrateTasks() {
  console.log('📦 Migrating tasks...');

  const tasks = sqliteDb.prepare('SELECT * FROM tasks').all() as Array<{
    id: string;
    title: string;
    description: string | null;
    type: string;
    status: string;
    priority: string;
    projectId: string;
    sprintId: string | null;
    createdAt: string;
    updatedAt: string;
    createdById: string | null;
    createdByName: string | null;
    createdByAvatarUrl: string | null;
    updatedById: string | null;
    updatedByName: string | null;
    updatedByAvatarUrl: string | null;
    assigneeId: string | null;
    assigneeName: string | null;
    assigneeEmail: string | null;
    assigneeAvatarUrl: string | null;
    dueDate: string | null;
    comments: string | null;
    tags: string | null;
    attachments: string | null;
  }>;

  let migrated = 0;
  let failed = 0;

  for (const task of tasks) {
    const projectUuid = projectIdMap.get(task.projectId);
    if (!projectUuid) {
      console.log(`  ⚠️ Skipping task ${task.title} - unknown project: ${task.projectId}`);
      continue;
    }

    const sprintUuid = task.sprintId ? sprintIdMap.get(task.sprintId) : null;
    const createdByUuid = task.createdById ? userIdMap.get(task.createdById) : null;
    const updatedByUuid = task.updatedById ? userIdMap.get(task.updatedById) : null;
    const assigneeUuid = task.assigneeId ? userIdMap.get(task.assigneeId) : null;

    // Parse JSON fields safely
    let comments = [];
    let tags = [];
    let attachments = [];

    try {
      comments = task.comments ? JSON.parse(task.comments) : [];
      tags = task.tags ? JSON.parse(task.tags) : [];
      attachments = task.attachments ? JSON.parse(task.attachments) : [];
    } catch (e) {
      console.warn(`  ⚠️ Failed to parse JSON for task ${task.id}:`, e);
    }

    const { error } = await supabase
      .from('tasks')
      .upsert({
        id: generateUUIDFromString(task.id),
        legacy_id: task.id,
        title: task.title,
        description: task.description,
        type: task.type,
        status: task.status,
        priority: task.priority,
        project_id: projectUuid,
        sprint_id: sprintUuid,
        created_at: task.createdAt,
        updated_at: task.updatedAt,
        created_by_id: createdByUuid,
        created_by_name: task.createdByName,
        created_by_avatar_url: task.createdByAvatarUrl,
        updated_by_id: updatedByUuid,
        updated_by_name: task.updatedByName,
        updated_by_avatar_url: task.updatedByAvatarUrl,
        assignee_id: assigneeUuid,
        assignee_name: task.assigneeName,
        assignee_email: task.assigneeEmail,
        assignee_avatar_url: task.assigneeAvatarUrl,
        due_date: task.dueDate,
        comments: comments,
        tags: tags,
        attachments: attachments,
      }, { onConflict: 'legacy_id' });

    if (error) {
      console.error(`  ❌ Failed to migrate task "${task.title}":`, error.message);
      failed++;
    } else {
      migrated++;
    }
  }

  console.log(`  ✅ Migrated ${migrated} tasks (${failed} failed)\n`);
  return migrated;
}

async function migrateMeta() {
  console.log('📦 Migrating meta data...');

  const meta = sqliteDb.prepare("SELECT * FROM meta WHERE key = 'lastUpdated'").get() as {
    key: string;
    value: string;
  } | undefined;

  if (meta) {
    const { error } = await supabase
      .from('meta')
      .upsert({
        key: 'lastUpdated',
        value: meta.value,
        updated_at: new Date().toISOString(),
      }, { onConflict: 'key' });

    if (error) {
      console.error(`  ❌ Failed to migrate meta:`, error.message);
    } else {
      console.log(`  ✅ Migrated lastUpdated: ${meta.value}\n`);
    }
  }
}

async function main() {
  console.log('🚀 Starting SQLite → Supabase migration\n');
  console.log(`Supabase URL: ${SUPABASE_URL}\n`);

  try {
    // Test connection
    const { error: healthError } = await supabase.from('users').select('count').limit(1);
    if (healthError && healthError.code !== 'PGRST116') { // PGRST116 = no rows, which is fine
      throw new Error(`Cannot connect to Supabase: ${healthError.message}`);
    }
    console.log('✅ Connected to Supabase\n');

    // Migration order matters due to foreign keys
    const stats = {
      users: await migrateUsers(),
      sessions: await migrateSessions(),
      passwordResetTokens: await migratePasswordResetTokens(),
      projects: await migrateProjects(),
      sprints: await migrateSprints(),
      tasks: await migrateTasks(),
    };

    await migrateMeta();

    console.log('═══════════════════════════════════════');
    console.log('✅ Migration Complete!');
    console.log('═══════════════════════════════════════');
    console.log(`  Users: ${stats.users}`);
    console.log(`  Sessions: ${stats.sessions}`);
    console.log(`  Password Reset Tokens: ${stats.passwordResetTokens}`);
    console.log(`  Projects: ${stats.projects}`);
    console.log(`  Sprints: ${stats.sprints}`);
    console.log(`  Tasks: ${stats.tasks}`);
    console.log('═══════════════════════════════════════');
    console.log('\nNext steps:');
    console.log('  1. Update your .env.local with Supabase credentials');
    console.log('  2. Test the app locally: npm run dev');
    console.log('  3. Deploy to Vercel with the new environment variables');

  } catch (error) {
    console.error('\n❌ Migration failed:', error);
    process.exit(1);
  } finally {
    sqliteDb.close();
  }
}

main();
