import { existsSync, readFileSync } from "node:fs";
import { basename, join } from "node:path";
import { homedir } from "node:os";
import { redactBrowserValue } from "../../redact";
import { createSkipCollector } from "../skipped";
import { type ActivityEvent, type ActivitySourceResult, type LandqSourceOptions } from "../types";

export const LANDQ_SOURCE = {
  id: "landq",
  label: "land queue",
  category: "land",
} as const;

export const LANDQ_SOURCE_ID = LANDQ_SOURCE.id;
const LANDQ_SOURCE_LABEL = LANDQ_SOURCE.label;

interface LandqRaw {
  ticket?: unknown;
  branch?: unknown;
  gate_class?: unknown;
  queue_depth_at_arrival?: unknown;
  at?: unknown;
}

export function defaultRepoRoots(): string[] {
  return [join(homedir(), "Projects", "overdeck")];
}

function asMessage(error: unknown): string {
  return error instanceof Error ? error.message : String(error);
}

function asString(value: unknown): string | undefined {
  if (typeof value === "string") {
    const trimmed = value.trim();
    return trimmed.length === 0 ? undefined : trimmed;
  }
  return undefined;
}

function asNumber(value: unknown): number | undefined {
  if (typeof value === "number" && Number.isFinite(value)) {
    return value;
  }
  return undefined;
}

function buildTitle(record: LandqRaw): string {
  const branch = asString(record.branch);
  const gateClass = asString(record.gate_class);
  const depth = asNumber(record.queue_depth_at_arrival);
  if (!branch) {
    if (gateClass === undefined) return "queued";
    return depth === undefined ? `queued (gate ${gateClass})` : `queued (gate ${gateClass}, depth ${depth})`;
  }
  if (gateClass === undefined) return `queued ${branch}`;
  return depth === undefined ? `queued ${branch} (gate ${gateClass})` : `queued ${branch} (gate ${gateClass}, depth ${depth})`;
}

export function readLandqSource(options: LandqSourceOptions = {}): ActivitySourceResult {
  const {
    repoRoots = defaultRepoRoots(),
    readFileImpl = (path) => readFileSync(path, "utf8"),
    existsSyncImpl = existsSync,
    fromMs,
    toMs,
    collectSkipped = false,
  } = options;

  const queuePaths = repoRoots.map((root) => join(root, ".git", "harness", "landq", "log"));
  const coveragePath = queuePaths.join(";");

  let records = 0;
  let totalRecords = 0;
  const skips = createSkipCollector(LANDQ_SOURCE_ID, collectSkipped);
  let earliestMs: number | undefined;
  let latestMs: number | undefined;
  let anyPathExists = false;
  const events: ActivityEvent[] = [];
  let ordinal = 0;
  let lineNumber = 0;

  for (const repoRoot of repoRoots) {
    const queuePath = join(repoRoot, ".git", "harness", "landq", "log");
    if (!existsSyncImpl(queuePath)) {
      continue;
    }
    anyPathExists = true;

    let raw: string;
    try {
      raw = readFileImpl(queuePath);
    } catch (error) {
      skips.record("unreadable-file", { path: queuePath }, undefined, asMessage(error));
      continue;
    }

    lineNumber = 0;
    for (const line of raw.split("\n")) {
      lineNumber += 1;
      const trimmed = line.trim();
      if (!trimmed) continue;
      ordinal += 1;
      const where = { path: queuePath, line: lineNumber };

      let parsed: unknown;
      try {
        parsed = JSON.parse(trimmed);
      } catch {
        skips.record("unparseable-json", where, trimmed);
        continue;
      }
      if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
        skips.record("not-an-object", where, trimmed);
        continue;
      }

      const record = parsed as LandqRaw;
      const ts = asString(record.at);
      if (!ts) {
        skips.record("missing-fields", where, trimmed, "at is absent or blank");
        continue;
      }

      const tsMs = Date.parse(ts);
      if (!Number.isFinite(tsMs)) {
        skips.record("invalid-timestamp", where, trimmed, `at=${ts}`);
        continue;
      }

      totalRecords += 1;
      if (earliestMs === undefined || tsMs < earliestMs) earliestMs = tsMs;
      if (latestMs === undefined || tsMs > latestMs) latestMs = tsMs;
      if (fromMs !== undefined && tsMs < fromMs) continue;
      if (toMs !== undefined && tsMs > toMs) continue;

      const ticket = asString(record.ticket);
      const project = asString(basename(repoRoot));
      const event: ActivityEvent = {
        id: `${LANDQ_SOURCE_ID}:${ordinal}`,
        ts,
        category: LANDQ_SOURCE.category,
        source: LANDQ_SOURCE_ID,
        actor: "agent",
        severity: "info",
        title: buildTitle(record),
      };
      if (project) {
        event.project = project;
      }
      if (ticket) {
        event.session = ticket;
      }

      const redacted = redactBrowserValue(parsed);
      if (redacted && typeof redacted === "object" && !Array.isArray(redacted)) {
        event.detail = redacted as Record<string, unknown>;
      }

      events.push(event);
      records += 1;
    }
  }

  if (!anyPathExists) {
    return {
      events: [],
      skippedEntries: [],
      skippedTruncated: false,
      coverage: {
        id: LANDQ_SOURCE_ID,
        label: LANDQ_SOURCE_LABEL,
        category: LANDQ_SOURCE.category,
        path: coveragePath,
        status: "absent",
        records: 0,
        totalRecords: 0,
        skipped: 0,
      },
    };
  }

  return {
    events,
    skippedEntries: skips.entries,
    skippedTruncated: skips.truncated,
    coverage: {
      id: LANDQ_SOURCE_ID,
      label: LANDQ_SOURCE_LABEL,
      category: LANDQ_SOURCE.category,
      path: coveragePath,
      status: "ok",
      records,
      totalRecords,
      skipped: skips.count,
      ...(earliestMs === undefined ? {} : {
        earliest: new Date(earliestMs).toISOString(),
        latest: new Date(latestMs ?? earliestMs).toISOString(),
      }),
    },
  };
}
