'use strict';
const path = require('path');
const fs = require('fs');
const db = require('../db');

db.initDb();
const d = db.getDb();
const home = require('os').homedir();
const claudeProjectsDir = path.join(home, '.claude', 'projects');

// 1. Scan all JSONL files + extract metadata (internal sessionId, first user message)
console.log('=== SCANNING JSONL FILES ===');
let totalFiles = 0;
const fileIndex = {}; // fileUuid -> { filePath, projectEntry, size, birthtime, mtime, internalSessionId }

for (const projectEntry of fs.readdirSync(claudeProjectsDir)) {
  const projectDir = path.join(claudeProjectsDir, projectEntry);
  let stat;
  try { stat = fs.statSync(projectDir); } catch { continue; }
  if (!stat.isDirectory()) continue;

  let files;
  try { files = fs.readdirSync(projectDir); } catch { continue; }

  for (const file of files) {
    if (!file.endsWith('.jsonl')) continue;
    const filePath = path.join(projectDir, file);
    const fileUuid = file.replace(/\.jsonl$/, '');
    const fstat = fs.statSync(filePath);

    // Extract internal sessionId + first user message from JSONL
    let internalSessionId = '', firstMsg = '';
    try {
      const fd = fs.openSync(filePath, 'r');
      const buf = Buffer.alloc(Math.min(fstat.size, 100000));
      fs.readSync(fd, buf, 0, buf.length, 0);
      fs.closeSync(fd);
      const lines = buf.toString('utf8').split('\n');
      for (const line of lines) {
        if (!line.trim()) continue;
        try {
          const entry = JSON.parse(line);
          if (entry.sessionId && !internalSessionId) internalSessionId = entry.sessionId;
          if (!firstMsg && entry.type === 'user' && entry.message?.role === 'user') {
            const c = entry.message.content;
            let text = typeof c === 'string' ? c : Array.isArray(c) ? c.filter(b => b.type === 'text').map(b => b.text).join(' ') : '';
            // Strip command boilerplate
            text = text.replace(/<command-message>[\s\S]*?<\/command-message>/g, '')
                       .replace(/<command-name>[\s\S]*?<\/command-name>/g, '')
                       .replace(/<command-args>[\s\S]*?<\/command-args>/g, '')
                       .trim();
            if (text.length >= 10 && !text.startsWith('Generate short, descriptive titles')) {
              firstMsg = text.slice(0, 80);
            }
          }
          if (internalSessionId && firstMsg) break;
        } catch {}
      }
    } catch {}

    fileIndex[fileUuid] = {
      filePath, projectEntry, internalSessionId, firstMsg,
      size: fstat.size,
      birthtime: fstat.birthtime.toISOString(),
      mtime: fstat.mtime.toISOString(),
      projectPath: projectEntry.replace(/-/g, path.sep).replace(/^\//, '')
    };
    totalFiles++;
  }
}
console.log('Total JSONL files found:', totalFiles);

// 2. Check startup_tasks for tab-to-agent mappings
const tasks = d.prepare('SELECT session_id, claude_session_id, claude_project_dir, cwd FROM startup_tasks').all();
console.log('startup_tasks entries:', tasks.length);
const tabToAgent = {};
for (const t of tasks) {
  if (t.claude_session_id && t.session_id !== t.claude_session_id) {
    tabToAgent[t.session_id] = { agentId: t.claude_session_id, projectDir: t.claude_project_dir, cwd: t.cwd };
  }
}
console.log('Tab-to-agent mappings:', Object.keys(tabToAgent).length);

// 3. Cross-reference sessions table
const allSessions = d.prepare('SELECT * FROM sessions').all();
console.log('\n=== SESSION TABLE ANALYSIS ===');
console.log('Total rows:', allSessions.length);

let hasFile = 0, noFile = 0, wrongTimestamp = 0, missingAgent = 0, missingFileData = 0;

for (const s of allSessions) {
  const file = fileIndex[s.id] || (s.agent_session_id ? fileIndex[s.agent_session_id] : null);
  if (file) {
    hasFile++;
    const dbCreated = new Date(s.created_at.replace(' ', 'T') + 'Z').getTime();
    const fileBirth = new Date(file.birthtime).getTime();
    if (Math.abs(dbCreated - fileBirth) > 60000) wrongTimestamp++;
    if (!s.file_size || s.file_size === 0) missingFileData++;
  } else {
    noFile++;
  }

  if (tabToAgent[s.id] && (!s.agent_session_id || s.agent_session_id === '' || s.agent_session_id === s.id)) {
    missingAgent++;
  }
}

console.log('Sessions with JSONL file:', hasFile);
console.log('Sessions WITHOUT JSONL file:', noFile);
console.log('Sessions with wrong created_at (>1min off from file birthtime):', wrongTimestamp);
console.log('Sessions missing file_size/modified_at:', missingFileData);
console.log('Sessions missing agent_session_id from startup_tasks:', missingAgent);

// 4. Files NOT in sessions table
let orphanFiles = 0;
const sessionIds = new Set(allSessions.map(s => s.id));
const agentIds = new Set(allSessions.filter(s => s.agent_session_id).map(s => s.agent_session_id));
const orphans = [];
for (const [sid, f] of Object.entries(fileIndex)) {
  if (!sessionIds.has(sid) && !agentIds.has(sid)) {
    orphanFiles++;
    if (orphans.length < 5) orphans.push({ id: sid.slice(0, 12), size: f.size, project: f.projectEntry.slice(-30) });
  }
}
console.log('\nJSONL files not in sessions table:', orphanFiles);
if (orphans.length > 0) console.log('  Sample orphans:', JSON.stringify(orphans));

// 5. Session_titles not in sessions
const titles = d.prepare('SELECT COUNT(*) as c FROM session_titles').get().c;
const titlesInSessions = d.prepare('SELECT COUNT(*) as c FROM session_titles WHERE session_id IN (SELECT id FROM sessions)').get().c;
console.log('\nSession titles total:', titles, '| In sessions table:', titlesInSessions, '| Orphan titles:', titles - titlesInSessions);

// === FIX MODE ===
if (process.argv.includes('--fix')) {
  console.log('\n=== APPLYING FIXES ===');

  const updateStmt = d.prepare(`
    UPDATE sessions SET
      agent_session_id = COALESCE(NULLIF(@agent_session_id, ''), agent_session_id),
      project_entry = COALESCE(NULLIF(@project_entry, ''), project_entry),
      project_path = COALESCE(NULLIF(@project_path, ''), project_path),
      cwd = COALESCE(NULLIF(@cwd, ''), cwd),
      jsonl_path = COALESCE(NULLIF(@jsonl_path, ''), jsonl_path),
      file_size = CASE WHEN @file_size > 0 THEN @file_size ELSE file_size END,
      modified_at = COALESCE(NULLIF(@modified_at, ''), modified_at),
      created_at = CASE WHEN @created_at != '' THEN @created_at ELSE created_at END,
      updated_at = datetime('now')
    WHERE id = @id
  `);

  const insertStmt = d.prepare(`
    INSERT OR IGNORE INTO sessions (id, agent_session_id, provider, project_entry, project_path, cwd, title, jsonl_path, file_size, modified_at, created_at)
    VALUES (@id, @agent_session_id, 'claude', @project_entry, @project_path, @cwd, @title, @jsonl_path, @file_size, @modified_at, @created_at)
  `);

  let updated = 0, inserted = 0, agentFixed = 0, titleTsFixed = 0, idMatched = 0;

  const txn = d.transaction(() => {
    // Fix 1: Update existing sessions with file data + correct timestamps
    for (const s of allSessions) {
      const file = fileIndex[s.id] || (s.agent_session_id ? fileIndex[s.agent_session_id] : null);
      if (!file) continue;

      const fileId = fileIndex[s.id] ? s.id : s.agent_session_id;
      const decodeProject = file.projectEntry.replace(/-/g, '/');
      const projectPath = '/' + decodeProject;

      updateStmt.run({
        id: s.id,
        agent_session_id: (fileId !== s.id) ? fileId : '',
        project_entry: file.projectEntry,
        project_path: projectPath,
        cwd: s.cwd || projectPath,
        jsonl_path: file.filePath,
        file_size: file.size,
        modified_at: file.mtime,
        created_at: file.birthtime.replace('T', ' ').replace(/\.\d+Z$/, ''),
      });
      updated++;
    }

    // Fix 2: Add agent_session_id from startup_tasks
    for (const [tabId, info] of Object.entries(tabToAgent)) {
      const row = d.prepare('SELECT id, agent_session_id FROM sessions WHERE id = ?').get(tabId);
      if (row && (!row.agent_session_id || row.agent_session_id === '' || row.agent_session_id === row.id)) {
        d.prepare('UPDATE sessions SET agent_session_id = ?, updated_at = datetime(\'now\') WHERE id = ?')
          .run(info.agentId, tabId);
        agentFixed++;
      }
    }

    // Fix 3: Insert orphan JSONL files
    for (const [sid, f] of Object.entries(fileIndex)) {
      if (sessionIds.has(sid) || agentIds.has(sid)) continue;
      // Read first user message for title
      let title = '';
      try {
        const content = fs.readFileSync(f.filePath, 'utf8');
        const lines = content.split('\n');
        for (const line of lines) {
          if (!line.trim()) continue;
          const entry = JSON.parse(line);
          if (entry.type === 'user' && entry.message?.role === 'user') {
            const c = entry.message.content;
            const text = typeof c === 'string' ? c : Array.isArray(c) ? c.filter(b => b.type === 'text').map(b => b.text).join(' ') : '';
            if (text) { title = text.slice(0, 80); break; }
          }
        }
      } catch {}

      const decodeProject = f.projectEntry.replace(/-/g, '/');
      const projectPath = '/' + decodeProject;
      insertStmt.run({
        id: sid,
        agent_session_id: '',
        project_entry: f.projectEntry,
        project_path: projectPath,
        cwd: projectPath,
        title,
        jsonl_path: f.filePath,
        file_size: f.size,
        modified_at: f.mtime,
        created_at: f.birthtime.replace('T', ' ').replace(/\.\d+Z$/, ''),
      });
      inserted++;
    }

    // Fix 4: Match no-file sessions to orphan JSONL files via internal sessionId
    // Some JSONL files record their original sessionId internally which may be the CTM tab ID
    const noFileIds = new Set(d.prepare("SELECT id FROM sessions WHERE (file_size = 0 OR file_size IS NULL)").all().map(r => r.id));
    const orphanFileIds = new Set();
    // Identify orphan-inserted rows (have file but no session_title)
    for (const row of d.prepare("SELECT s.id FROM sessions s LEFT JOIN session_titles st ON s.id = st.session_id WHERE s.file_size > 0 AND st.session_id IS NULL").all()) {
      orphanFileIds.add(row.id);
    }

    for (const [fileUuid, f] of Object.entries(fileIndex)) {
      if (!f.internalSessionId || f.internalSessionId === fileUuid) continue;
      if (!noFileIds.has(f.internalSessionId)) continue;
      if (!orphanFileIds.has(fileUuid)) continue;

      const decodeProject = f.projectEntry.replace(/-/g, '/');
      const projectPath = '/' + decodeProject;
      d.prepare(`UPDATE sessions SET agent_session_id=?, project_entry=?, project_path=?, jsonl_path=?, file_size=?, modified_at=?, created_at=?, updated_at=datetime('now') WHERE id=?`)
        .run(fileUuid, f.projectEntry, projectPath, f.filePath, f.size, f.mtime, f.birthtime.replace('T', ' ').replace(/\.\d+Z$/, ''), f.internalSessionId);
      d.prepare('DELETE FROM sessions WHERE id = ?').run(fileUuid);
      noFileIds.delete(f.internalSessionId);
      orphanFileIds.delete(fileUuid);
      idMatched++;
    }

    // Fix 5: For remaining no-file sessions, use session_titles.generated_at as fallback timestamp
    for (const noFileId of noFileIds) {
      const titleRow = d.prepare('SELECT generated_at FROM session_titles WHERE session_id = ?').get(noFileId);
      if (titleRow && titleRow.generated_at) {
        d.prepare("UPDATE sessions SET created_at = ?, updated_at = datetime('now') WHERE id = ?")
          .run(titleRow.generated_at, noFileId);
        titleTsFixed++;
      }
    }
  });

  txn();
  console.log(`Updated ${updated} existing rows with file data + corrected timestamps`);
  console.log(`Fixed ${agentFixed} missing agent_session_id from startup_tasks`);
  console.log(`Inserted ${inserted} orphan JSONL sessions`);
  console.log(`Merged ${idMatched} orphan files with CTM tabs via internal sessionId`);
  console.log(`Fixed ${titleTsFixed} no-file sessions with session_titles.generated_at timestamp`);

  // Verify
  const newTotal = d.prepare('SELECT COUNT(*) as c FROM sessions').get().c;
  const withSizeNow = d.prepare('SELECT COUNT(*) as c FROM sessions WHERE file_size > 0').get().c;
  const withModNow = d.prepare("SELECT COUNT(*) as c FROM sessions WHERE modified_at != ''").get().c;
  console.log(`\n=== POST-FIX STATS ===`);
  console.log(`Total sessions: ${newTotal}`);
  console.log(`With file_size: ${withSizeNow}`);
  console.log(`With modified_at: ${withModNow}`);

  // Verify timestamp consistency (UTC-aware)
  let goodTs = 0, badTs = 0;
  const postRows = d.prepare("SELECT id, agent_session_id, created_at FROM sessions WHERE file_size > 0").all();
  for (const r of postRows) {
    const file = fileIndex[r.id] || fileIndex[r.agent_session_id];
    if (!file) continue;
    // DB stores UTC without T/Z — parse as UTC explicitly
    const dbMs = new Date(r.created_at.replace(' ', 'T') + 'Z').getTime();
    const fileMs = new Date(file.birthtime).getTime();
    if (Math.abs(dbMs - fileMs) < 60000) goodTs++;
    else badTs++;
  }
  console.log(`Timestamp consistency: ${goodTs} good, ${badTs} bad (>1min off)`);
}

db.closeDb();
