/**
 * GitLab Issue Scanner — sibling of issue-scanner.ts for GitHub.
 *
 * Per-project config (table: issue_autofix_gitlab_config):
 *   - enabled: boolean
 *   - interval: minutes (0 = manual only)
 *   - labels: string[]   filter by label (empty = all)
 *   - baseBranch: ''     default '' (auto-resolve from milestone/label/desc/default)
 *   - baseBranchRule: 'milestone' | 'label:<prefix>' | 'desc' | 'default'
 *   - mrTitleTemplate: string
 *   - mrBodyTemplate: string
 *
 * Triggers the `gitlab-issue-fix-and-review` builtin pipeline.
 */

import { execSync } from 'node:child_process';
import { getDb } from '../src/core/db/database';
import { getDbPath } from '../src/config';
import { startPipeline } from './pipeline';

function db() { return getDb(getDbPath()); }

export interface GitLabAutofixConfig {
  projectPath: string;
  projectName: string;
  enabled: boolean;
  interval: number;
  labels: string[];
  baseBranch: string;
  baseBranchRule: string;          // 'milestone' | 'label:<prefix>' | 'desc' | 'default'
  mrTitleTemplate: string;
  mrBodyTemplate: string;
  assigneeFilter: string;          // 'me' | <username> | '' (no filter)
}

// ── DB schema ────────────────────────────────────────────

export function ensureTable() {
  db().exec(`
    CREATE TABLE IF NOT EXISTS issue_autofix_gitlab_config (
      project_path TEXT PRIMARY KEY,
      project_name TEXT NOT NULL,
      enabled INTEGER NOT NULL DEFAULT 0,
      interval_min INTEGER NOT NULL DEFAULT 30,
      labels TEXT NOT NULL DEFAULT '[]',
      base_branch TEXT NOT NULL DEFAULT '',
      base_branch_rule TEXT NOT NULL DEFAULT 'milestone',
      mr_title_template TEXT NOT NULL DEFAULT '',
      mr_body_template TEXT NOT NULL DEFAULT '',
      assignee_filter TEXT NOT NULL DEFAULT 'me',
      last_scan_at TEXT
    );
    CREATE TABLE IF NOT EXISTS issue_autofix_gitlab_processed (
      project_path TEXT NOT NULL,
      issue_iid INTEGER NOT NULL,
      pipeline_id TEXT,
      mr_iid INTEGER,
      status TEXT NOT NULL DEFAULT 'processing',
      created_at TEXT NOT NULL DEFAULT (datetime('now')),
      PRIMARY KEY (project_path, issue_iid)
    );
  `);
}

// ── CRUD ────────────────────────────────────────────────

export function getConfig(projectPath: string): GitLabAutofixConfig | null {
  ensureTable();
  const row = db().prepare('SELECT * FROM issue_autofix_gitlab_config WHERE project_path = ?').get(projectPath) as any;
  if (!row) return null;
  return {
    projectPath: row.project_path,
    projectName: row.project_name,
    enabled: !!row.enabled,
    interval: row.interval_min,
    labels: JSON.parse(row.labels || '[]'),
    baseBranch: row.base_branch || '',
    baseBranchRule: row.base_branch_rule || 'milestone',
    mrTitleTemplate: row.mr_title_template || '',
    mrBodyTemplate: row.mr_body_template || '',
    assigneeFilter: row.assignee_filter || 'me',
  };
}

export function saveConfig(c: GitLabAutofixConfig): void {
  ensureTable();
  db().prepare(`
    INSERT OR REPLACE INTO issue_autofix_gitlab_config
      (project_path, project_name, enabled, interval_min, labels, base_branch, base_branch_rule, mr_title_template, mr_body_template, assignee_filter)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(
    c.projectPath, c.projectName, c.enabled ? 1 : 0, c.interval,
    JSON.stringify(c.labels || []), c.baseBranch || '', c.baseBranchRule || 'milestone',
    c.mrTitleTemplate || '', c.mrBodyTemplate || '', c.assigneeFilter || 'me',
  );
  refreshTimer(c);
}

export function listConfigs(): GitLabAutofixConfig[] {
  ensureTable();
  return (db().prepare('SELECT * FROM issue_autofix_gitlab_config').all() as any[]).map(r => ({
    projectPath: r.project_path, projectName: r.project_name,
    enabled: !!r.enabled, interval: r.interval_min,
    labels: JSON.parse(r.labels || '[]'),
    baseBranch: r.base_branch || '', baseBranchRule: r.base_branch_rule || 'milestone',
    mrTitleTemplate: r.mr_title_template || '', mrBodyTemplate: r.mr_body_template || '',
    assigneeFilter: r.assignee_filter || 'me',
  }));
}

// ── GitLab remote parsing ──────────────────────────────

function gitRemote(projectPath: string): string | null {
  try {
    return execSync(`git -C "${projectPath}" remote get-url origin`, { encoding: 'utf8', timeout: 3000 }).trim();
  } catch { return null; }
}

/** Resolve <namespace>/<project> path from origin URL. Returns null if not a GitLab remote we can talk to. */
function projectPathFromRemote(remote: string): string | null {
  // git@host:group/sub/project.git  OR  https://host/group/sub/project(.git)
  const cleaned = remote
    .replace(/^https?:\/\/[^/]+\//, '')
    .replace(/^git@[^:]+:/, '')
    .replace(/\.git$/, '');
  if (!cleaned.includes('/')) return null;
  return cleaned;
}

// ── Fetch issues via glab ──────────────────────────────

function fetchOpenIssues(projectPath: string, c: GitLabAutofixConfig): { iid: number; title: string; error?: string }[] {
  const remote = gitRemote(projectPath);
  if (!remote) return [{ iid: -1, title: '', error: 'No git remote configured' }];
  const projPath = projectPathFromRemote(remote);
  if (!projPath) return [{ iid: -1, title: '', error: `Unrecognized git remote: ${remote}` }];

  const labelFilter = c.labels.length > 0 ? ` --label "${c.labels.join(',')}"` : '';
  const assigneeFilter = c.assigneeFilter === 'me' ? ' --assignee="@me"'
    : c.assigneeFilter ? ` --assignee="${c.assigneeFilter}"` : '';

  try {
    const out = execSync(
      `glab issue list --state opened -R "${projPath}"${labelFilter}${assigneeFilter} --output json`,
      { encoding: 'utf8', timeout: 30000, cwd: projectPath },
    );
    const parsed = JSON.parse(out);
    return (Array.isArray(parsed) ? parsed : []).map((it: any) => ({
      iid: Number(it.iid ?? it.id ?? -1),
      title: it.title || '',
    }));
  } catch (e: any) {
    const msg = e.stderr?.toString() || e.message || 'glab CLI failed';
    if (/not authenticated|auth/i.test(msg)) {
      return [{ iid: -1, title: '', error: 'glab CLI not authenticated. Run: glab auth login' }];
    }
    if (/glab.*not found|command not found/i.test(msg)) {
      return [{ iid: -1, title: '', error: 'glab CLI not installed. brew install glab && glab auth login' }];
    }
    return [{ iid: -1, title: '', error: msg }];
  }
}

// ── Dedup state ────────────────────────────────────────

function isProcessed(projectPath: string, iid: number): boolean {
  return !!db().prepare('SELECT 1 FROM issue_autofix_gitlab_processed WHERE project_path = ? AND issue_iid = ?').get(projectPath, iid);
}

function markProcessed(projectPath: string, iid: number, pipelineId: string): void {
  db().prepare(`
    INSERT OR REPLACE INTO issue_autofix_gitlab_processed (project_path, issue_iid, pipeline_id, status)
    VALUES (?, ?, ?, 'processing')
  `).run(projectPath, iid, pipelineId);
}

export function updateProcessedStatus(projectPath: string, iid: number, status: string, mrIid?: number): void {
  if (mrIid != null) {
    db().prepare('UPDATE issue_autofix_gitlab_processed SET status = ?, mr_iid = ? WHERE project_path = ? AND issue_iid = ?')
      .run(status, mrIid, projectPath, iid);
  } else {
    db().prepare('UPDATE issue_autofix_gitlab_processed SET status = ? WHERE project_path = ? AND issue_iid = ?')
      .run(status, projectPath, iid);
  }
}

export function resetProcessedIssue(projectPath: string, iid: number): void {
  db().prepare('DELETE FROM issue_autofix_gitlab_processed WHERE project_path = ? AND issue_iid = ?').run(projectPath, iid);
}

export function getProcessedIssues(projectPath: string): { issueIid: number; pipelineId: string; mrIid: number | null; status: string; createdAt: string }[] {
  ensureTable();
  return (db().prepare('SELECT * FROM issue_autofix_gitlab_processed WHERE project_path = ? ORDER BY created_at DESC').all(projectPath) as any[]).map(r => ({
    issueIid: r.issue_iid, pipelineId: r.pipeline_id,
    mrIid: r.mr_iid, status: r.status, createdAt: r.created_at,
  }));
}

// ── Scan + trigger ─────────────────────────────────────

function saveLastScan(projectPath: string) {
  const now = Date.now();
  scannerState.lastScan.set(projectPath, now);
  try { db().prepare('UPDATE issue_autofix_gitlab_config SET last_scan_at = ? WHERE project_path = ?').run(new Date(now).toISOString(), projectPath); } catch {}
}

function loadLastScan(projectPath: string): number | null {
  try {
    const row = db().prepare('SELECT last_scan_at FROM issue_autofix_gitlab_config WHERE project_path = ?').get(projectPath) as any;
    return row?.last_scan_at ? new Date(row.last_scan_at).getTime() : null;
  } catch { return null; }
}

export function scanAndTrigger(c: GitLabAutofixConfig): { triggered: number; issues: number[]; total: number; error?: string } {
  saveLastScan(c.projectPath);
  const issues = fetchOpenIssues(c.projectPath, c);
  if (issues.length === 1 && (issues[0] as any).error) {
    return { triggered: 0, issues: [], total: 0, error: (issues[0] as any).error };
  }
  const triggered: number[] = [];
  for (const it of issues) {
    if (it.iid < 0 || isProcessed(c.projectPath, it.iid)) continue;
    try {
      const pipeline = startPipeline('gitlab-issue-fix-and-review', {
        issue_id: String(it.iid),
        project: c.projectName,
        base_branch: c.baseBranch || 'auto',
        base_branch_rule: c.baseBranchRule || 'milestone',
        mr_title_template: c.mrTitleTemplate || '',
        mr_body_template: c.mrBodyTemplate || '',
      });
      markProcessed(c.projectPath, it.iid, pipeline.id);
      triggered.push(it.iid);
      console.log(`[gl-issue-scanner] Triggered fix for !${it.iid} "${it.title}" in ${c.projectName} (pipeline: ${pipeline.id})`);
    } catch (e) {
      console.error(`[gl-issue-scanner] Failed to trigger for !${it.iid}:`, e);
    }
  }
  return { triggered: triggered.length, issues: triggered, total: issues.length };
}

// ── Periodic scanner ───────────────────────────────────

const scannerKey = Symbol.for('forge-issue-scanner-gitlab');
const gAny = globalThis as any;
if (!gAny[scannerKey]) gAny[scannerKey] = { timers: new Map<string, NodeJS.Timeout>(), started: false, lastScan: new Map<string, number>() };
const scannerState = gAny[scannerKey] as { timers: Map<string, NodeJS.Timeout>; started: boolean; lastScan: Map<string, number> };

function refreshTimer(c: GitLabAutofixConfig) {
  const existing = scannerState.timers.get(c.projectPath);
  if (existing) { clearInterval(existing); scannerState.timers.delete(c.projectPath); }
  if (!c.enabled || c.interval <= 0) return;
  const intervalMs = c.interval * 60 * 1000;
  const dbLast = loadLastScan(c.projectPath);
  if (dbLast) scannerState.lastScan.set(c.projectPath, dbLast);
  else scannerState.lastScan.set(c.projectPath, Date.now());
  const elapsed = Date.now() - (scannerState.lastScan.get(c.projectPath) || 0);
  const firstDelay = Math.max(0, intervalMs - elapsed);
  setTimeout(() => {
    try { const cfg = getConfig(c.projectPath); if (cfg?.enabled) scanAndTrigger(cfg); } catch {}
    const timer = setInterval(() => {
      const cfg = getConfig(c.projectPath);
      if (!cfg || !cfg.enabled) return;
      try { scanAndTrigger(cfg); } catch {}
    }, intervalMs);
    scannerState.timers.set(c.projectPath, timer);
  }, firstDelay);
}

export function startScanner() {
  if (scannerState.started) return;
  scannerState.started = true;
  ensureTable();
  for (const c of listConfigs()) refreshTimer(c);
}

export function stopScanner() {
  for (const t of scannerState.timers.values()) clearInterval(t);
  scannerState.timers.clear();
  scannerState.started = false;
}
