import { APP_CONFIG_TABLE_SCHEMA_SQL, LAST_SCANNED_AT_SQL, PROJECTS_TABLE_SCHEMA_SQL, QUEUED_MESSAGES_TABLE_SCHEMA_SQL, PUSH_SUBSCRIPTIONS_TABLE_SCHEMA_SQL, SESSIONS_TABLE_SCHEMA_SQL, USER_NOTIFICATION_PREFERENCES_TABLE_SCHEMA_SQL, VAPID_KEYS_TABLE_SCHEMA_SQL, } from '../../modules/database/schema.js';
const SQLITE_UUID_SQL = `
lower(hex(randomblob(4))) || '-' ||
lower(hex(randomblob(2))) || '-' ||
lower(hex(randomblob(2))) || '-' ||
lower(hex(randomblob(2))) || '-' ||
lower(hex(randomblob(6)))
`;
const addColumnToTableIfNotExists = (db, tableName, columnNames, columnName, columnType) => {
    if (!columnNames.includes(columnName)) {
        console.log(`Running migration: Adding ${columnName} column to ${tableName} table`);
        db.exec(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnType}`);
    }
};
const tableExists = (db, tableName) => Boolean(db
    .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
    .get(tableName));
const getTableInfo = (db, tableName) => db.prepare(`PRAGMA table_info(${tableName})`).all();
const migrateLegacySessionNames = (db) => {
    const hasLegacySessionNamesTable = tableExists(db, 'session_names');
    const hasSessionsTable = tableExists(db, 'sessions');
    if (!hasLegacySessionNamesTable) {
        return;
    }
    if (hasSessionsTable) {
        console.log('Running migration: Merging session_names into sessions');
        db.exec(`
      INSERT INTO sessions (session_id, user_id, provider, custom_name, created_at, updated_at)
      SELECT
        session_id,
        COALESCE(
          (SELECT id FROM users WHERE is_active = 1 ORDER BY id LIMIT 1),
          1
        ),
        COALESCE(provider, 'claude'),
        custom_name,
        COALESCE(created_at, CURRENT_TIMESTAMP),
        COALESCE(updated_at, CURRENT_TIMESTAMP)
      FROM session_names
      WHERE true
      ON CONFLICT(user_id, session_id) DO UPDATE SET
        provider = excluded.provider,
        custom_name = COALESCE(excluded.custom_name, sessions.custom_name),
        created_at = COALESCE(sessions.created_at, excluded.created_at),
        updated_at = COALESCE(excluded.updated_at, sessions.updated_at)
    `);
        db.exec('DROP TABLE session_names');
        return;
    }
    console.log('Running migration: Renaming session_names table to sessions');
    db.exec('ALTER TABLE session_names RENAME TO sessions');
};
const migrateLegacyWorkspaceTableIntoProjects = (db) => {
    db.exec(PROJECTS_TABLE_SCHEMA_SQL);
    if (!tableExists(db, 'workspace_original_paths')) {
        return;
    }
    console.log('Running migration: Migrating workspace_original_paths data into projects');
    db.exec(`
    INSERT INTO projects (project_id, user_id, project_path, custom_project_name, isStarred, isArchived)
    SELECT
      CASE
        WHEN workspace_id IS NULL OR trim(workspace_id) = ''
        THEN ${SQLITE_UUID_SQL}
        ELSE workspace_id
      END,
      COALESCE(
        (SELECT id FROM users WHERE is_active = 1 ORDER BY id LIMIT 1),
        1
      ),
      workspace_path,
      custom_workspace_name,
      COALESCE(isStarred, 0),
      0
    FROM workspace_original_paths
    WHERE workspace_path IS NOT NULL AND trim(workspace_path) <> ''
    ON CONFLICT(user_id, project_path) DO UPDATE SET
      custom_project_name = COALESCE(projects.custom_project_name, excluded.custom_project_name),
      isStarred = COALESCE(projects.isStarred, excluded.isStarred)
  `);
};
const rebuildProjectsTableWithPrimaryKeySchema = (db) => {
    const hasProjectsTable = tableExists(db, 'projects');
    if (!hasProjectsTable) {
        db.exec(PROJECTS_TABLE_SCHEMA_SQL);
        return;
    }
    const projectsTableInfo = getTableInfo(db, 'projects');
    const columnNames = projectsTableInfo.map((column) => column.name);
    const hasProjectIdPrimaryKey = projectsTableInfo.some((column) => column.name === 'project_id' && column.pk === 1);
    const shouldRebuild = !hasProjectIdPrimaryKey || !columnNames.includes('user_id');
    if (!shouldRebuild) {
        addColumnToTableIfNotExists(db, 'projects', columnNames, 'custom_project_name', 'TEXT DEFAULT NULL');
        addColumnToTableIfNotExists(db, 'projects', columnNames, 'isStarred', 'BOOLEAN DEFAULT 0');
        addColumnToTableIfNotExists(db, 'projects', columnNames, 'isArchived', 'BOOLEAN DEFAULT 0');
        db.exec(`
      UPDATE projects
      SET project_id = ${SQLITE_UUID_SQL}
      WHERE project_id IS NULL OR trim(project_id) = ''
    `);
        return;
    }
    console.log('Running migration: Rebuilding projects table to enforce user_id + project_id primary key schema');
    const projectPathExpression = columnNames.includes('project_path')
        ? 'project_path'
        : columnNames.includes('workspace_path')
            ? 'workspace_path'
            : 'NULL';
    const userIdExpression = columnNames.includes('user_id')
        ? 'user_id'
        : 'COALESCE((SELECT id FROM users WHERE is_active = 1 ORDER BY id LIMIT 1), 1)';
    const customProjectNameExpression = columnNames.includes('custom_project_name')
        ? 'custom_project_name'
        : columnNames.includes('custom_workspace_name')
            ? 'custom_workspace_name'
            : 'NULL';
    const isStarredExpression = columnNames.includes('isStarred') ? 'COALESCE(isStarred, 0)' : '0';
    const isArchivedExpression = columnNames.includes('isArchived') ? 'COALESCE(isArchived, 0)' : '0';
    const projectIdExpression = columnNames.includes('project_id')
        ? `CASE
         WHEN project_id IS NULL OR trim(project_id) = ''
         THEN ${SQLITE_UUID_SQL}
         ELSE project_id
       END`
        : SQLITE_UUID_SQL;
    db.exec('PRAGMA foreign_keys = OFF');
    try {
        db.exec('BEGIN TRANSACTION');
        db.exec('DROP TABLE IF EXISTS projects__new');
        db.exec(`
      CREATE TABLE projects__new (
        project_id TEXT PRIMARY KEY NOT NULL,
        user_id INTEGER NOT NULL,
        project_path TEXT NOT NULL,
        custom_project_name TEXT DEFAULT NULL,
        isStarred BOOLEAN DEFAULT 0,
        isArchived BOOLEAN DEFAULT 0,
        FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
        UNIQUE(user_id, project_path)
      )
    `);
        db.exec(`
      WITH source_rows AS (
        SELECT
          ${userIdExpression} AS user_id,
          ${projectPathExpression} AS project_path,
          ${customProjectNameExpression} AS custom_project_name,
          ${isStarredExpression} AS isStarred,
          ${isArchivedExpression} AS isArchived,
          ${projectIdExpression} AS candidate_project_id,
          rowid AS source_rowid
        FROM projects
        WHERE ${projectPathExpression} IS NOT NULL AND trim(${projectPathExpression}) <> ''
      ),
      deduped_paths AS (
        SELECT
          user_id,
          project_path,
          custom_project_name,
          isStarred,
          isArchived,
          candidate_project_id,
          source_rowid,
          ROW_NUMBER() OVER (PARTITION BY user_id, project_path ORDER BY source_rowid) AS project_path_rank
        FROM source_rows
      ),
      prepared_rows AS (
        SELECT
          CASE
            WHEN ROW_NUMBER() OVER (PARTITION BY candidate_project_id ORDER BY source_rowid) = 1
            THEN candidate_project_id
            ELSE ${SQLITE_UUID_SQL}
          END AS project_id,
          user_id,
          project_path,
          custom_project_name,
          isStarred,
          isArchived
        FROM deduped_paths
        WHERE project_path_rank = 1
      )
      INSERT INTO projects__new (
        project_id,
        user_id,
        project_path,
        custom_project_name,
        isStarred,
        isArchived
      )
      SELECT
        project_id,
        user_id,
        project_path,
        custom_project_name,
        isStarred,
        isArchived
      FROM prepared_rows
    `);
        db.exec('DROP TABLE projects');
        db.exec('ALTER TABLE projects__new RENAME TO projects');
        db.exec('COMMIT');
    }
    catch (migrationError) {
        db.exec('ROLLBACK');
        throw migrationError;
    }
    finally {
        db.exec('PRAGMA foreign_keys = ON');
    }
};
const rebuildSessionsTableWithProjectSchema = (db) => {
    const hasSessions = tableExists(db, 'sessions');
    if (!hasSessions) {
        db.exec(SESSIONS_TABLE_SCHEMA_SQL);
        return;
    }
    const sessionsTableInfo = getTableInfo(db, 'sessions');
    const columnNames = sessionsTableInfo.map((column) => column.name);
    const primaryKeyColumns = sessionsTableInfo
        .filter((column) => column.pk > 0)
        .sort((a, b) => a.pk - b.pk)
        .map((column) => column.name);
    const shouldRebuild = !columnNames.includes('project_path') ||
        !columnNames.includes('user_id') ||
        primaryKeyColumns.length !== 2 ||
        primaryKeyColumns[0] !== 'user_id' ||
        primaryKeyColumns[1] !== 'session_id' ||
        !columnNames.includes('provider');
    if (!shouldRebuild) {
        addColumnToTableIfNotExists(db, 'sessions', columnNames, 'jsonl_path', 'TEXT');
        addColumnToTableIfNotExists(db, 'sessions', columnNames, 'isArchived', 'BOOLEAN DEFAULT 0');
        addColumnToTableIfNotExists(db, 'sessions', columnNames, 'created_at', 'DATETIME');
        addColumnToTableIfNotExists(db, 'sessions', columnNames, 'updated_at', 'DATETIME');
        db.exec('UPDATE sessions SET isArchived = COALESCE(isArchived, 0)');
        db.exec('UPDATE sessions SET created_at = COALESCE(created_at, CURRENT_TIMESTAMP)');
        db.exec('UPDATE sessions SET updated_at = COALESCE(updated_at, CURRENT_TIMESTAMP)');
        return;
    }
    console.log('Running migration: Rebuilding sessions table to project-based schema');
    const projectPathExpression = columnNames.includes('project_path')
        ? 'project_path'
        : columnNames.includes('workspace_path')
            ? 'workspace_path'
            : 'NULL';
    const providerExpression = columnNames.includes('provider')
        ? "COALESCE(provider, 'claude')"
        : "'claude'";
    const userIdExpression = columnNames.includes('user_id')
        ? 'user_id'
        : 'COALESCE((SELECT id FROM users WHERE is_active = 1 ORDER BY id LIMIT 1), 1)';
    const customNameExpression = columnNames.includes('custom_name')
        ? 'custom_name'
        : 'NULL';
    const jsonlPathExpression = columnNames.includes('jsonl_path')
        ? 'jsonl_path'
        : 'NULL';
    const isArchivedExpression = columnNames.includes('isArchived')
        ? 'COALESCE(isArchived, 0)'
        : '0';
    const createdAtExpression = columnNames.includes('created_at')
        ? 'COALESCE(created_at, CURRENT_TIMESTAMP)'
        : 'CURRENT_TIMESTAMP';
    const updatedAtExpression = columnNames.includes('updated_at')
        ? 'COALESCE(updated_at, CURRENT_TIMESTAMP)'
        : 'CURRENT_TIMESTAMP';
    db.exec('PRAGMA foreign_keys = OFF');
    try {
        db.exec('BEGIN TRANSACTION');
        db.exec('DROP TABLE IF EXISTS sessions__new');
        db.exec(`
      CREATE TABLE sessions__new (
        session_id TEXT NOT NULL,
        user_id INTEGER NOT NULL,
        provider TEXT NOT NULL DEFAULT 'claude',
        custom_name TEXT,
        project_path TEXT,
        jsonl_path TEXT,
        isArchived BOOLEAN DEFAULT 0,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (user_id, session_id),
        FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
        FOREIGN KEY (user_id, project_path) REFERENCES projects(user_id, project_path)
        ON DELETE CASCADE
        ON UPDATE CASCADE
      )
    `);
        db.exec(`
      WITH source_rows AS (
        SELECT
          session_id,
          ${userIdExpression} AS user_id,
          ${providerExpression} AS provider,
          ${customNameExpression} AS custom_name,
          ${projectPathExpression} AS project_path,
          ${jsonlPathExpression} AS jsonl_path,
          ${isArchivedExpression} AS isArchived,
          ${createdAtExpression} AS created_at,
          ${updatedAtExpression} AS updated_at,
          rowid AS source_rowid
        FROM sessions
        WHERE session_id IS NOT NULL AND trim(session_id) <> ''
      ),
      ranked_rows AS (
        SELECT
          session_id,
          user_id,
          provider,
          custom_name,
          project_path,
          jsonl_path,
          isArchived,
          created_at,
          updated_at,
          ROW_NUMBER() OVER (
            PARTITION BY user_id, session_id
            ORDER BY datetime(COALESCE(updated_at, created_at)) DESC, source_rowid DESC
          ) AS session_rank
        FROM source_rows
      )
      INSERT INTO sessions__new (
        session_id,
        user_id,
        provider,
        custom_name,
        project_path,
        jsonl_path,
        isArchived,
        created_at,
        updated_at
      )
      SELECT
        session_id,
        user_id,
        provider,
        custom_name,
        project_path,
        jsonl_path,
        isArchived,
        created_at,
        updated_at
      FROM ranked_rows
      WHERE session_rank = 1
    `);
        db.exec('DROP TABLE sessions');
        db.exec('ALTER TABLE sessions__new RENAME TO sessions');
        db.exec('COMMIT');
    }
    catch (migrationError) {
        db.exec('ROLLBACK');
        throw migrationError;
    }
    finally {
        db.exec('PRAGMA foreign_keys = ON');
    }
};
const ensureProjectsForSessionPaths = (db) => {
    if (!tableExists(db, 'sessions')) {
        return;
    }
    db.exec(`
    INSERT INTO projects (project_id, user_id, project_path, custom_project_name, isStarred, isArchived)
    SELECT
      ${SQLITE_UUID_SQL},
      user_id,
      project_path,
      NULL,
      0,
      0
    FROM sessions
    WHERE project_path IS NOT NULL AND trim(project_path) <> ''
    ON CONFLICT(user_id, project_path) DO NOTHING
  `);
};
export const runMigrations = (db) => {
    try {
        const usersTableInfo = db.prepare('PRAGMA table_info(users)').all();
        const userColumnNames = usersTableInfo.map((column) => column.name);
        addColumnToTableIfNotExists(db, 'users', userColumnNames, 'git_name', 'TEXT');
        addColumnToTableIfNotExists(db, 'users', userColumnNames, 'git_email', 'TEXT');
        addColumnToTableIfNotExists(db, 'users', userColumnNames, 'has_completed_onboarding', 'BOOLEAN DEFAULT 0');
        addColumnToTableIfNotExists(db, 'users', userColumnNames, 'is_admin', 'BOOLEAN DEFAULT 0');
        db.exec("UPDATE users SET is_admin = 1 WHERE id = 1 AND is_active = 1");
        db.exec(APP_CONFIG_TABLE_SCHEMA_SQL);
        db.exec(USER_NOTIFICATION_PREFERENCES_TABLE_SCHEMA_SQL);
        db.exec(VAPID_KEYS_TABLE_SCHEMA_SQL);
        db.exec(PUSH_SUBSCRIPTIONS_TABLE_SCHEMA_SQL);
        db.exec('CREATE INDEX IF NOT EXISTS idx_push_subscriptions_user_id ON push_subscriptions(user_id)');
        db.exec(PROJECTS_TABLE_SCHEMA_SQL);
        rebuildProjectsTableWithPrimaryKeySchema(db);
        migrateLegacyWorkspaceTableIntoProjects(db);
        rebuildSessionsTableWithProjectSchema(db);
        migrateLegacySessionNames(db);
        ensureProjectsForSessionPaths(db);
        db.exec('CREATE INDEX IF NOT EXISTS idx_session_ids_lookup ON sessions(user_id, session_id)');
        db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id)');
        db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_project_path ON sessions(user_id, project_path)');
        db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_is_archived ON sessions(isArchived)');
        db.exec('CREATE INDEX IF NOT EXISTS idx_projects_user_id ON projects(user_id)');
        db.exec('CREATE INDEX IF NOT EXISTS idx_projects_user_path ON projects(user_id, project_path)');
        db.exec('CREATE INDEX IF NOT EXISTS idx_projects_is_starred ON projects(isStarred)');
        db.exec('CREATE INDEX IF NOT EXISTS idx_projects_is_archived ON projects(isArchived)');
        db.exec(QUEUED_MESSAGES_TABLE_SCHEMA_SQL);
        db.exec('CREATE INDEX IF NOT EXISTS idx_queued_messages_session ON queued_messages(user_id, session_id, created_at)');
        db.exec('DROP INDEX IF EXISTS idx_session_names_lookup');
        db.exec('DROP INDEX IF EXISTS idx_sessions_workspace_path');
        db.exec('DROP INDEX IF EXISTS idx_workspace_original_paths_is_starred');
        db.exec('DROP INDEX IF EXISTS idx_workspace_original_paths_workspace_id');
        if (tableExists(db, 'workspace_original_paths')) {
            console.log('Running migration: Dropping legacy workspace_original_paths table');
            db.exec('DROP TABLE workspace_original_paths');
        }
        db.exec(LAST_SCANNED_AT_SQL);
        console.log('Database migrations completed successfully');
    }
    catch (error) {
        console.error('Error running migrations:', error.message);
        throw error;
    }
};
//# sourceMappingURL=migrations.js.map