import fs from 'node:fs';
import path from 'node:path';
import { ROOT, ARTIFACTS_DIR, DATA_DIR } from './paths.js';
import { getDb } from './db.js';
import { getProject, DEFAULT_PROJECT_ID } from './projects.js';
import { artifactsDirForProject } from './artifact-paths.js';

const MARKER_FILE = path.join(DATA_DIR, '.artifacts-migrated-v1');

function alreadyMigrated() {
  try { return fs.existsSync(MARKER_FILE); } catch { return false; }
}

function writeMarker(summary) {
  try {
    fs.writeFileSync(
      MARKER_FILE,
      `${new Date().toISOString()}\n${JSON.stringify(summary, null, 2)}\n`,
      'utf8'
    );
  } catch {}
}

function relToRoot(absPath) {
  return path.relative(ROOT, absPath);
}

function resolveProjectForRow(row) {
  const projectId = row.project_id || DEFAULT_PROJECT_ID;
  return getProject(projectId) || getProject(DEFAULT_PROJECT_ID);
}

export function migrateArtifactsToProjects({ force = false, log = () => {} } = {}) {
  if (!force && alreadyMigrated()) {
    return { ok: true, skipped: true, reason: 'already_migrated' };
  }

  const db = getDb();
  let rows = [];
  try {
    rows = db.prepare(
      "SELECT id, path, project_id FROM artifacts WHERE path IS NOT NULL AND path != ''"
    ).all();
  } catch {
    return { ok: false, error: 'artifacts table unavailable', summary: null };
  }

  const summary = { moved: 0, rewritten: 0, missing_on_disk: 0, already_scoped: 0, errors: 0, details: [] };
  const legacyAbs = path.resolve(ARTIFACTS_DIR);
  const legacyPrefix = legacyAbs + path.sep;

  db.exec('BEGIN IMMEDIATE');
  try {
    for (const row of rows) {
      const currentAbs = path.resolve(ROOT, row.path);
      const project = resolveProjectForRow(row);
      if (!project) { summary.errors++; continue; }

      const targetDir = artifactsDirForProject(project);
      const targetDirAbs = path.resolve(targetDir);

      if (currentAbs === targetDirAbs || currentAbs.startsWith(targetDirAbs + path.sep)) {
        summary.already_scoped++;
        continue;
      }
      if (!(currentAbs === legacyAbs || currentAbs.startsWith(legacyPrefix))) {
        summary.already_scoped++;
        continue;
      }

      try { fs.mkdirSync(targetDir, { recursive: true }); } catch {}
      const filename = path.basename(currentAbs);
      const targetAbs = path.join(targetDir, filename);
      const targetRel = relToRoot(targetAbs);

      try {
        if (fs.existsSync(currentAbs)) {
          let finalAbs = targetAbs;
          let finalRel = targetRel;
          let collision = false;
          if (fs.existsSync(targetAbs) && currentAbs !== targetAbs) {
            const ext = path.extname(filename);
            const base = path.basename(filename, ext);
            const stamp = Date.now().toString(36);
            finalAbs = path.join(targetDir, `${base}-${stamp}${ext}`);
            finalRel = relToRoot(finalAbs);
            collision = true;
          }
          fs.renameSync(currentAbs, finalAbs);
          db.prepare('UPDATE artifacts SET path = ?, project_id = ? WHERE id = ?')
            .run(finalRel, project.id, row.id);
          summary.moved++;
          summary.rewritten++;
          summary.details.push({ id: row.id, from: row.path, to: finalRel, collision });
        } else {
          summary.missing_on_disk++;
          db.prepare('UPDATE artifacts SET path = ?, project_id = ? WHERE id = ?')
            .run(targetRel, project.id, row.id);
          summary.rewritten++;
          summary.details.push({ id: row.id, from: row.path, to: targetRel, file_missing: true });
        }
      } catch (err) {
        summary.errors++;
        log(`migrate-artifacts: failed for ${row.id} (${row.path}): ${err.message}`);
      }
    }
    db.exec('COMMIT');
  } catch (err) {
    try { db.exec('ROLLBACK'); } catch {}
    log(`migrate-artifacts: rolled back: ${err.message}`);
    return { ok: false, error: err.message, summary };
  }

  writeMarker(summary);
  return { ok: true, skipped: false, summary };
}
