import fs from "fs";
import path from "path";

const RESULT_LOGS_DIRNAME = "logs";
const RESULT_SETUP_DIRNAME = "setup";

export function createRunLogRegistry(productDir) {
  const serviceRecords = new Map();
  const setupRecords = new Map();

  return {
    ensureServiceLogRecord(config) {
      const key = `${config.runtimeLabel || config.name}:${config.name}`;
      const existing = serviceRecords.get(key);
      if (existing) return existing;

      const fileName = `${sanitizePathSegment(config.runtimeLabel || config.name)}__${sanitizePathSegment(config.name)}.log`;
      const relativePath = path.join(".testkit", "results", RESULT_LOGS_DIRNAME, fileName);
      const absolutePath = path.join(productDir, relativePath);
      fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
      const stream = fs.createWriteStream(absolutePath, {
        fd: fs.openSync(absolutePath, "a"),
        flags: "a",
      });
      const record = {
        key,
        serviceName: config.name,
        runtimeLabel: config.runtimeLabel || config.name,
        path: normalizePath(relativePath),
        absolutePath,
        stream,
      };
      serviceRecords.set(key, record);
      return record;
    },
    ensureSetupLogRecord(config, stage) {
      const key = `${config.runtimeLabel || config.name}:${config.name}:${stage}`;
      const existing = setupRecords.get(key);
      if (existing) return existing;

      const fileName = `${sanitizePathSegment(config.runtimeLabel || config.name)}__${sanitizePathSegment(config.name)}__${sanitizePathSegment(stage)}.log`;
      const relativePath = path.join(".testkit", "results", RESULT_SETUP_DIRNAME, fileName);
      const absolutePath = path.join(productDir, relativePath);
      fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
      const stream = fs.createWriteStream(absolutePath, {
        fd: fs.openSync(absolutePath, "a"),
        flags: "a",
      });
      const record = {
        key,
        serviceName: config.name,
        runtimeLabel: config.runtimeLabel || config.name,
        stage,
        path: normalizePath(relativePath),
        absolutePath,
        stream,
      };
      setupRecords.set(key, record);
      return record;
    },
    append(record, streamName, line) {
      if (!record || typeof line !== "string") return;
      record.stream.write(`${new Date().toISOString()} [${streamName}] ${line}\n`);
    },
    listServiceLogs() {
      return [...serviceRecords.values()]
        .map((record) => ({
          serviceName: record.serviceName,
          runtimeLabel: record.runtimeLabel,
          path: record.path,
        }))
        .sort(
          (left, right) =>
            left.serviceName.localeCompare(right.serviceName) ||
            left.runtimeLabel.localeCompare(right.runtimeLabel)
        );
    },
    listSetupLogs() {
      return [...setupRecords.values()]
        .map((record) => ({
          serviceName: record.serviceName,
          runtimeLabel: record.runtimeLabel,
          stage: record.stage,
          path: record.path,
        }))
        .sort(
          (left, right) =>
            left.serviceName.localeCompare(right.serviceName) ||
            left.runtimeLabel.localeCompare(right.runtimeLabel) ||
            left.stage.localeCompare(right.stage)
        );
    },
    closeAll() {
      for (const record of serviceRecords.values()) {
        record.stream.end();
      }
      for (const record of setupRecords.values()) {
        record.stream.end();
      }
    },
  };
}

export function readLogTail(absolutePath, lineCount = 80) {
  if (!absolutePath || !fs.existsSync(absolutePath)) return [];
  const lines = fs.readFileSync(absolutePath, "utf8").split(/\r?\n/).filter(Boolean);
  return lines.slice(Math.max(0, lines.length - lineCount));
}

export function findLogSliceByRequestId(absolutePath, requestId, contextLines = 2) {
  if (!absolutePath || !requestId || !fs.existsSync(absolutePath)) return [];
  const lines = fs.readFileSync(absolutePath, "utf8").split(/\r?\n/).filter(Boolean);
  const matches = [];

  for (let index = 0; index < lines.length; index += 1) {
    if (!lines[index].includes(requestId)) continue;
    const start = Math.max(0, index - contextLines);
    const end = Math.min(lines.length, index + contextLines + 1);
    for (let cursor = start; cursor < end; cursor += 1) {
      if (!matches.includes(lines[cursor])) matches.push(lines[cursor]);
    }
  }

  return matches;
}

function sanitizePathSegment(value) {
  return String(value)
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9._-]+/g, "-")
    .replace(/^-+|-+$/g, "") || "log";
}

function normalizePath(filePath) {
  return filePath.split(path.sep).join("/");
}
