import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";

import { activeReportLifecycle } from "../../internal/dev-report-lifecycle.mjs";
import { DEV_ENDPOINT, DEV_FRONTEND_ENDPOINT, ENVIRONMENT_DEV } from "../../internal/protocol/index.mjs";

export const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");

const blockerTypes = new Set([
  "contract_blocker",
  "environment_blocker",
  "network_blocker",
  "runtime_blocker",
  "agent_blocker",
  "observability_blocker",
  "safety_blocker"
]);

const validationCommands = Object.freeze([
  "node scripts/repo-reports-guard.mjs",
  "node --check scripts/dev-evidence-blocker-aggregator.mjs",
  "node --check scripts/src/dev-evidence-blocker-aggregator.mjs",
  "node scripts/dev-evidence-blocker-aggregator.mjs --check",
  "node scripts/dev-evidence-blocker-aggregator.mjs --markdown",
  "node --check scripts/validate-dev-gate-report.mjs",
  "node scripts/validate-dev-gate-report.mjs"
]);

function issue(id) {
  return `pikasTech/HWLAB#${id}`;
}

function gitCommit() {
  try {
    return execFileSync("git", ["rev-parse", "--short=12", "HEAD"], {
      cwd: repoRoot,
      encoding: "utf8",
      stdio: ["ignore", "pipe", "ignore"]
    }).trim();
  } catch {
    return "unknown";
  }
}

function sourceEntry({ milestone, issue: issueId, level, status, category, sources, commands, summary }) {
  return {
    milestone,
    issue: issueId,
    level,
    status,
    category,
    lifecycleState: "active",
    sources,
    commands,
    summary
  };
}

function groupByLevel(evidence, blockers) {
  const grouped = Object.fromEntries(["SOURCE", "LOCAL", "DRY-RUN", "DEV-LIVE", "BLOCKED"].map((level) => [level, []]));
  for (const entry of evidence) {
    grouped[entry.level]?.push({
      milestone: entry.milestone,
      category: entry.category,
      status: entry.status,
      summary: entry.summary
    });
  }
  for (const blocker of blockers) {
    grouped.BLOCKED.push({
      milestone: blocker.milestone ?? "M5",
      category: blocker.scope,
      status: blocker.status,
      summary: blocker.summary
    });
  }
  return grouped;
}

function governanceBlocker() {
  return {
    id: "repo-report-directory:removed",
    priority: "P0",
    unblockOrder: 1,
    type: "observability_blocker",
    scope: "repo-report-directory-removed",
    status: "open",
    sourceIssue: issue(411),
    summary: "The repository report directory has been removed; durable progress and acceptance evidence must be recorded in GitHub issue or PR comments, with temporary JSON limited to /tmp, .state, or CI artifacts.",
    nextTask: "Rerun any live or CD checks with explicit non-repository output paths and post the operational conclusion to the owning issue or PR."
  };
}

function runtimeDurableBlocker() {
  return {
    id: "m4:runtime-durable-adapter",
    priority: "P1",
    unblockOrder: 2,
    type: "runtime_blocker",
    scope: "runtime-durable-adapter",
    status: "open",
    sourceIssue: issue(164),
    summary: "M4/M5 remain blocked until cloud-api durable runtime readiness proves schema, migration, and read-query durability; DB live evidence alone is not acceptance.",
    nextTask: "Repair runtime durability and rerun the read-only M4/M5 checks with non-repository output."
  };
}

function skillsBlocker() {
  return {
    id: "m4:skills-commit-version-injection",
    priority: "P1",
    unblockOrder: 3,
    type: "agent_blocker",
    scope: "skills-commit-version-injection",
    status: "open",
    sourceIssue: issue(164),
    summary: "M4/M5 agent evidence still requires deployed manager, skills, and worker paths to expose explicit skills commit and version.",
    nextTask: "Inject and surface skills commit/version on the live DEV agent stack before claiming agent-loop acceptance."
  };
}

function hardwareLoopBlocker() {
  return {
    id: "m3:m3-hardware-loop-runtime",
    priority: "P0",
    unblockOrder: 4,
    type: "runtime_blocker",
    scope: "m3-hardware-loop-runtime",
    status: "open",
    sourceIssue: issue(38),
    summary: "M3 remains blocked until the real DEV hardware trusted loop proves DO1 to patch-panel to DI1 with operation, trace, audit, and evidence identifiers.",
    nextTask: "Run the DEV M3 trusted-loop smoke after topology repair and record the conclusion on the owning GitHub issue."
  };
}

function buildDoD(blockers) {
  const checks = [
    {
      id: "repo-report-directory-absent",
      status: "pass",
      evidenceLevel: "SOURCE",
      summary: "Repository report directory is forbidden and guarded by repo checks."
    },
    {
      id: "cloud-api-db-ready",
      status: "blocked",
      evidenceLevel: "BLOCKED",
      summary: "Current checked source cannot claim DB readiness from deleted repository report files; rerun live readiness and post the result outside the repo."
    },
    {
      id: "runtime-durable-ready",
      status: "blocked",
      evidenceLevel: "BLOCKED",
      summary: "Durable adapter migration/query readiness still blocks M4/M5 promotion."
    },
    {
      id: "m3-trusted-loop-ready",
      status: "blocked",
      evidenceLevel: "BLOCKED",
      summary: "M3 trusted-loop live operation evidence is still required."
    }
  ];
  return {
    status: blockers.length === 0 ? "pass" : "blocked",
    green: blockers.length === 0,
    checks
  };
}

function buildCurrentDevLayering() {
  return {
    publicRoute: {
      label: "DEV public route",
      status: "blocked",
      evidenceLevel: "BLOCKED",
      summary: "Repository-stored route snapshots were removed; only live checks or issue/PR comments may establish current route state.",
      nextRequired: "Run read-only route checks with /tmp, .state, or CI artifact output."
    },
    dbLive: {
      label: "Cloud API DB live",
      status: "blocked",
      evidenceLevel: "BLOCKED",
      summary: "Deleted repository report files cannot be used as DB-live acceptance evidence.",
      nextRequired: "Rerun the current read-only health check and record the conclusion in GitHub."
    },
    runtimeDurability: {
      label: "Runtime durability",
      status: "blocked",
      evidenceLevel: "BLOCKED",
      summary: "Durable runtime adapter readiness remains the current M4/M5 blocker.",
      nextRequired: "Prove schema, migration, and read-query readiness."
    },
    skillsInjection: {
      label: "Skills commit/version injection",
      status: "blocked",
      evidenceLevel: "BLOCKED",
      summary: "Skills commit/version visibility remains required before agent-loop promotion.",
      nextRequired: "Expose skills commit and version through live DEV manager/worker health."
    },
    m3TrustedLoop: {
      label: "M3 trusted loop",
      status: "blocked",
      evidenceLevel: "BLOCKED",
      summary: "M3 must prove the real trusted loop through HWLAB runtime, not a generated file.",
      nextRequired: "Capture operation, trace, audit, and evidence identifiers in the owning issue."
    }
  };
}

function buildMilestones() {
  return [
    {
      id: "M0",
      status: "pass",
      highestVisibleLevel: "SOURCE",
      liveEvidence: false,
      summary: "Source contracts remain parseable."
    },
    {
      id: "M1",
      status: "pass",
      highestVisibleLevel: "LOCAL",
      liveEvidence: false,
      summary: "Local smoke remains source/local only."
    },
    {
      id: "M2",
      status: "blocked",
      highestVisibleLevel: "BLOCKED",
      liveEvidence: false,
      summary: "M2 live route and artifact conclusions must be rerun without repository report outputs."
    },
    {
      id: "M3",
      status: "blocked",
      highestVisibleLevel: "BLOCKED",
      liveEvidence: false,
      summary: "M3 trusted-loop live evidence remains required."
    },
    {
      id: "M4",
      status: "blocked",
      highestVisibleLevel: "BLOCKED",
      liveEvidence: false,
      summary: "M4 remains blocked by durable runtime readiness and skills injection."
    },
    {
      id: "M5",
      status: "blocked",
      highestVisibleLevel: "BLOCKED",
      liveEvidence: false,
      summary: "M5 cannot promote until M3/M4 blockers clear and current live checks are recorded outside the repo."
    }
  ];
}

function buildMilestoneLevelClassification(milestones) {
  return milestones.map((milestone) => ({
    milestone: milestone.id,
    currentLevel: milestone.highestVisibleLevel,
    status: milestone.status,
    strongestEvidenceLevel: milestone.highestVisibleLevel,
    liveEvidence: milestone.liveEvidence,
    summary: milestone.summary
  }));
}

function buildMilestoneBlockerClassification() {
  return [
    {
      milestone: "M3",
      status: "blocked",
      currentLevel: "BLOCKED",
      blockerClass: "m3-hardware-loop-runtime",
      dependency: "M3 trusted hardware loop operation, trace, audit, and evidence identifiers",
      nonPromotionReason: "M3 live trusted-loop evidence is not currently established after repository report removal.",
      nextRequired: "Repair topology and rerun the live M3 trusted-loop smoke with non-repository output."
    },
    {
      milestone: "M4",
      status: "blocked",
      currentLevel: "BLOCKED",
      blockerClass: "runtime_durable_adapter_query_blocked",
      dependency: "durable runtime adapter readiness and skill commit and version injection",
      nonPromotionReason: "M4 cannot promote without durable agent runtime readiness and skill version injection.",
      nextRequired: "Repair runtime durability and skills metadata, then rerun M4 read-only preflight."
    },
    {
      milestone: "M5",
      status: "blocked",
      currentLevel: "BLOCKED",
      blockerClass: "m3-m4-current-blockers",
      dependency: "runtime durable adapter readiness, skills commit/version injection, and M3 trusted-loop evidence",
      nonPromotionReason: "M5 cannot promote while M3/M4 blockers remain open and current evidence is not recorded in GitHub.",
      nextRequired: "Clear M3/M4 blockers and post live DEV acceptance conclusions to the owning issues or PR."
    }
  ];
}

function buildEvidence() {
  return [
    sourceEntry({
      milestone: "M0",
      issue: issue(31),
      category: "contract",
      level: "SOURCE",
      status: "pass",
      sources: ["protocol/README.md", "docs/reference/MVP-e2e-acceptance.md"],
      commands: ["node scripts/validate-contract.mjs"],
      summary: "Frozen contracts remain the source-ready baseline."
    }),
    sourceEntry({
      milestone: "M1",
      issue: issue(7),
      category: "local-smoke",
      level: "LOCAL",
      status: "pass",
      sources: ["scripts/validate-contract.mjs"],
      commands: ["node scripts/validate-contract.mjs"],
      summary: "Contract validation remains local-only source evidence."
    }),
    sourceEntry({
      milestone: "M5",
      issue: issue(411),
      category: "repository-governance",
      level: "SOURCE",
      status: "pass",
      sources: ["scripts/repo-reports-guard.mjs", "package.json", "scripts/gitops-render.mjs"],
      commands: ["node scripts/repo-reports-guard.mjs"],
      summary: "Repository report files are removed and guarded against recurrence."
    })
  ];
}

function buildNextSteps(blockers) {
  return blockers.map((blocker, index) => ({
    order: index + 1,
    action: blocker.nextTask,
    evidenceRequired: "Post the real operational conclusion in the owning GitHub issue or PR; do not commit generated report files."
  }));
}

export async function buildReport() {
  const blockers = [
    governanceBlocker(),
    runtimeDurableBlocker(),
    skillsBlocker(),
    hardwareLoopBlocker()
  ];
  const milestones = buildMilestones();
  const evidence = buildEvidence();
  const dod = buildDoD(blockers);
  const currentDevLayering = buildCurrentDevLayering();
  const milestoneLevelClassification = buildMilestoneLevelClassification(milestones);
  const milestoneBlockerClassification = buildMilestoneBlockerClassification();
  const report = {
    "$schema": "https://hwlab.pikastech.local/schemas/dev-m5-gate-aggregator-v2.schema.json",
    "$id": "https://hwlab.pikastech.local/dev-m5-gate-aggregator-v2.json",
    reportVersion: "v2",
    reportKind: "dev-m5-gate-aggregator",
    issue: issue(58),
    supports: [issue(7), issue(9), issue(23), issue(31), issue(37), issue(38), issue(39), issue(58), issue(411)],
    generatedAt: new Date().toISOString(),
    generatedFromCommit: gitCommit(),
    environment: ENVIRONMENT_DEV,
    endpoint: DEV_ENDPOINT,
    frontendEndpoint: DEV_FRONTEND_ENDPOINT,
    activeEndpoints: {
      frontend: `${DEV_FRONTEND_ENDPOINT}/`,
      apiLive: `${DEV_ENDPOINT}/health/live`
    },
    devOnly: true,
    prodDisabled: true,
    reportLifecycle: activeReportLifecycle(
      "Current aggregator is governance-only and does not persist evidence in repository report files."
    ),
    safety: {
      reportOnly: true,
      noDeploy: true,
      noProd: true,
      noSecretRead: true,
      noRuntimeRestart: true,
      noLiveProbe: true,
      noHeavyE2E: true,
      noRepoReportDirectory: true,
      noUniDeskRuntimeSubstitute: true
    },
    sourceReports: {
      status: "removed",
      policy: "Generated progress and acceptance snapshots are not source files. Use GitHub issue or PR comments plus temporary /tmp, .state, or CI artifact output."
    },
    overall: {
      status: "blocked",
      green: false,
      reason: "The old repository report mechanism has been removed. M3/M4/M5 still require real DEV checks and GitHub-hosted conclusions before any green acceptance claim."
    },
    latestFrontendDevFact: {
      revision: "not_claimed_by_repo_report",
      issue: issue(7),
      supports: [issue(7), issue(411)],
      endpoint: `${DEV_FRONTEND_ENDPOINT}/`,
      evidenceLevel: "BLOCKED",
      promotesM3M4M5: false,
      summary: "Frontend visibility must be established by current live checks or issue/PR comments, not by committed report snapshots."
    },
    dod,
    currentDevLayering,
    milestoneLevelClassification,
    milestoneBlockerClassification,
    milestones,
    evidence,
    levels: groupByLevel(evidence, blockers),
    blockers,
    nextSteps: buildNextSteps(blockers),
    validationCommands
  };
  validateReport(report);
  return report;
}

export function validateReport(report) {
  assert.equal(report.reportKind, "dev-m5-gate-aggregator");
  assert.equal(report.overall.status, "blocked");
  assert.equal(report.overall.green, false);
  assert.deepEqual(
    report.milestoneBlockerClassification.map((item) => item.milestone),
    ["M3", "M4", "M5"],
    "milestoneBlockerClassification"
  );
  assert.ok(report.blockers.length >= 1, "must remain blocked without live DEV acceptance");
  for (const blocker of report.blockers) {
    assert.ok(blockerTypes.has(blocker.type), `unknown blocker type ${blocker.type}`);
    assert.match(blocker.priority, /^P[0-3]$/u, `invalid priority ${blocker.priority}`);
    assert.equal(blocker.status, "open", "blocker status");
  }
}

export function formatCheckSummary(report) {
  return {
    ok: true,
    taskId: report.reportKind,
    issue: report.issue,
    status: report.overall.status,
    green: report.overall.green,
    blockers: report.blockers.length,
    activeEndpoints: report.activeEndpoints,
    m3m4m5: Object.fromEntries(
      report.milestoneBlockerClassification.map((item) => [item.milestone, item.status])
    ),
    nextStep: report.nextSteps[0]?.action ?? null,
    levels: Object.fromEntries(
      Object.entries(report.levels).map(([level, entries]) => [level, entries.length])
    )
  };
}

export function renderMarkdown(report) {
  const layeringLines = Object.entries(report.currentDevLayering)
    .map(([, layer]) => `| ${layer.label} | ${layer.status} | ${layer.evidenceLevel} | ${layer.summary} | ${layer.nextRequired} |`)
    .join("\n");
  const milestoneLines = report.milestoneBlockerClassification
    .map((item) => `| ${item.milestone} | ${item.status} | ${item.currentLevel} | ${item.blockerClass} | ${item.dependency} | ${item.nextRequired} |`)
    .join("\n");
  const blockerLines = report.blockers
    .map((blocker) => `| ${blocker.priority} | ${blocker.unblockOrder} | ${blocker.type} | ${blocker.scope} | ${blocker.summary} |`)
    .join("\n");
  const dodLines = report.dod.checks
    .map((check) => `| ${check.id} | ${check.status} | ${check.evidenceLevel} | ${check.summary} |`)
    .join("\n");

  return `# HWLAB M5 DEV Gate Aggregator v2

Status: ${report.overall.status}
Generated from: \`${report.generatedFromCommit}\`
Scope: DEV only, no repository report output
Active frontend: \`${report.activeEndpoints.frontend}\`
Active API/live: \`${report.activeEndpoints.apiLive}\`

## Summary

${report.overall.reason}

## Current DEV Layering

| Layer | Status | Evidence level | Current conclusion | Required next proof |
| --- | --- | --- | --- | --- |
${layeringLines}

## DoD Checks

| Check | Status | Evidence level | Summary |
| --- | --- | --- | --- |
${dodLines}

## M3/M4/M5 Blocker Classification

| Milestone | Status | Current level | Blocker class | Dependency | Required next proof |
| --- | --- | --- | --- | --- | --- |
${milestoneLines}

## Blockers

| Priority | Order | Type | Scope | Summary |
| --- | ---: | --- | --- | --- |
${blockerLines}

## Validation

${report.validationCommands.map((command) => `- \`${command}\``).join("\n")}

## Boundary

This aggregator is governance-only. It reads source contracts, does not deploy, does not call DEV or PROD, does not read secrets, and does not persist generated evidence in the repository.
`;
}
