import { promises as fs } from 'node:fs';
import path from 'node:path';

import { loadMission } from './mission.js';
import { parseTasksFile } from './tasks-file.js';
import type {
  Mission,
  MissionSession,
  MissionStatusSummary,
  MissionTask,
  TaskDetail,
} from './types.js';

const SESSION_LOCK_FILE = 'session.lock';

interface SessionLockState {
  session_id?: string;
  runtime?: string;
  pid?: number;
  started_at?: string;
  milestone_id?: string;
}

function tasksFilePath(mission: Mission): string {
  if (path.isAbsolute(mission.tasksFile)) {
    return mission.tasksFile;
  }
  return path.join(mission.projectPath, mission.tasksFile);
}

function sessionLockPath(mission: Mission): string {
  const orchestratorDir = path.isAbsolute(mission.orchestratorDir)
    ? mission.orchestratorDir
    : path.join(mission.projectPath, mission.orchestratorDir);

  return path.join(orchestratorDir, SESSION_LOCK_FILE);
}

function isPidAlive(pid: number): boolean {
  try {
    process.kill(pid, 0);
    return true;
  } catch {
    return false;
  }
}

async function readTasks(mission: Mission): Promise<MissionTask[]> {
  try {
    const content = await fs.readFile(tasksFilePath(mission), 'utf8');
    return parseTasksFile(content);
  } catch (error) {
    if (
      typeof error === 'object' &&
      error !== null &&
      'code' in error &&
      (error as { code?: string }).code === 'ENOENT'
    ) {
      return [];
    }
    throw error;
  }
}

async function readActiveSession(mission: Mission): Promise<MissionSession | undefined> {
  let lockRaw: string;
  try {
    lockRaw = await fs.readFile(sessionLockPath(mission), 'utf8');
  } catch (error) {
    if (
      typeof error === 'object' &&
      error !== null &&
      'code' in error &&
      (error as { code?: string }).code === 'ENOENT'
    ) {
      return undefined;
    }
    throw error;
  }

  let lock: SessionLockState;
  try {
    lock = JSON.parse(lockRaw) as SessionLockState;
  } catch {
    return undefined;
  }
  if (
    typeof lock.session_id !== 'string' ||
    (lock.runtime !== 'claude' && lock.runtime !== 'codex') ||
    typeof lock.started_at !== 'string'
  ) {
    return undefined;
  }

  const pid = typeof lock.pid === 'number' ? lock.pid : undefined;
  if (pid !== undefined && pid > 0 && !isPidAlive(pid)) {
    return undefined;
  }

  const existingSession = mission.sessions.find((session) => session.sessionId === lock.session_id);
  if (existingSession !== undefined) {
    return existingSession;
  }

  return {
    sessionId: lock.session_id,
    runtime: lock.runtime,
    pid,
    startedAt: lock.started_at,
    milestoneId: lock.milestone_id,
  };
}

async function buildStatusSummary(
  freshMission: Mission,
  tasks: MissionTask[],
): Promise<MissionStatusSummary> {
  const done = tasks.filter((task) => task.status === 'done').length;
  const inProgress = tasks.filter((task) => task.status === 'in-progress').length;
  const pending = tasks.filter((task) => task.status === 'not-started').length;
  const blocked = tasks.filter((task) => task.status === 'blocked').length;
  const cancelled = tasks.filter((task) => task.status === 'cancelled').length;
  const nextTask = tasks.find((task) => task.status === 'not-started');

  const completedMilestones = freshMission.milestones.filter(
    (milestone) => milestone.status === 'completed',
  ).length;
  const currentMilestone =
    freshMission.milestones.find((milestone) => milestone.status === 'in-progress') ??
    freshMission.milestones.find((milestone) => milestone.status === 'pending');

  const activeSession = await readActiveSession(freshMission);

  return {
    mission: {
      id: freshMission.id,
      name: freshMission.name,
      status: freshMission.status,
      projectPath: freshMission.projectPath,
    },
    milestones: {
      total: freshMission.milestones.length,
      completed: completedMilestones,
      current: currentMilestone,
    },
    tasks: {
      total: tasks.length,
      done,
      inProgress,
      pending,
      blocked,
      cancelled,
    },
    nextTaskId: nextTask?.id,
    activeSession,
  };
}

export async function getMissionStatus(mission: Mission): Promise<MissionStatusSummary> {
  const freshMission = await loadMission(mission.projectPath);
  const tasks = await readTasks(freshMission);
  return buildStatusSummary(freshMission, tasks);
}

export async function getTaskStatus(mission: Mission, taskId: string): Promise<TaskDetail> {
  const freshMission = await loadMission(mission.projectPath);
  const tasks = await readTasks(freshMission);

  const matches = tasks.filter((task) => task.id === taskId);
  if (matches.length === 0) {
    throw new Error(`Task not found: ${taskId}`);
  }

  if (matches.length > 1) {
    throw new Error(`Duplicate task IDs found: ${taskId}`);
  }

  const summary = await buildStatusSummary(freshMission, tasks);

  return {
    missionId: freshMission.id,
    task: matches[0]!,
    isNextTask: summary.nextTaskId === taskId,
    activeSession: summary.activeSession,
  };
}
