import type { DecisionScope } from "../../schema/src/decisions/scope.js";
import { conflictPayloadSchema, draftPayloadSchema, parseContract } from "./report-schema.js";
import { readReportClientConfig, type ReviewDraftView, type ReviewVerdict } from "./review-types.js";

export type FinalizationResultView = {
  decisionCounts: { confirmedDefect: number; notDefect: number };
  packetPath?: string;
  unresolvedCount: number;
  verificationCommand: string;
};

let expectedRevision = 0;

export class ReviewConflictError extends Error {
  constructor(readonly changedDecisions: ReviewDraftView["decisions"], readonly draft: ReviewDraftView) {
    super(`Review conflict. Changed decisions: ${changedDecisions.map((decision) => `${decision.findingId} (${decision.verdict})`).join(", ")}. Merge and retry.`);
  }
}

function authHeaders(): Record<string, string> {
  const token = readReportClientConfig().apiToken;
  if (token === undefined) {
    throw new Error("report review API is unavailable without a capability token");
  }
  return {
    "Content-Type": "application/json",
    "X-Report-Token": token,
    "X-Report-CSRF": token,
  };
}

async function parseDraftResponse(response: Response): Promise<ReviewDraftView> {
  if (response.status === 409) {
    const payload = parseContract(conflictPayloadSchema, await response.json(), "review conflict") as { draft: ReviewDraftView; changedDecisions: ReviewDraftView["decisions"] };
    expectedRevision = payload.draft.revision;
    throw new ReviewConflictError(payload.changedDecisions, payload.draft);
  }
  if (!response.ok) {
    const payload = (await response.json().catch(() => ({}))) as { message?: string };
    throw new Error(payload.message ?? `review API failed: ${String(response.status)}`);
  }
  const payload = parseContract(draftPayloadSchema, await response.json(), "review draft") as { draft: ReviewDraftView };
  expectedRevision = payload.draft.revision;
  return payload.draft;
}

export async function fetchReviewDraft(): Promise<ReviewDraftView> {
  const response = await fetch("/api/draft");
  if (!response.ok) {
    if (response.status === 404) {
      return {
        schemaVersion: "review-draft/v1",
        revision: 0,
        decisions: [],
        notes: {},
        undoStacks: {},
      };
    }
    const payload = (await response.json().catch(() => ({}))) as { message?: string };
    throw new Error(payload.message ?? `failed to load review draft: ${String(response.status)}`);
  }
  const payload = parseContract(draftPayloadSchema, await response.json(), "review draft") as { draft: ReviewDraftView };
  expectedRevision = payload.draft.revision;
  return payload.draft;
}

export async function postReviewMutation(body: Record<string, unknown>): Promise<ReviewDraftView> {
  const response = await fetch("/api/review", {
    method: "POST",
    headers: authHeaders(),
    body: JSON.stringify({ ...body, expectedRevision }),
  });
  return parseDraftResponse(response);
}

export async function decideFinding(input: {
  findingId: string;
  verdict: ReviewVerdict;
  reason?: string;
  scope?: DecisionScope;
}): Promise<ReviewDraftView> {
  return postReviewMutation({
    action: "decide",
    targetIds: [input.findingId],
    verdict: input.verdict,
    ...(input.reason === undefined ? {} : { reason: input.reason }),
    ...(input.scope === undefined ? {} : { scope: input.scope }),
  });
}

export async function batchDecide(input: {
  findingIds: string[];
  verdict: ReviewVerdict;
}): Promise<ReviewDraftView> {
  return postReviewMutation({
    action: "batch-decide",
    targetIds: input.findingIds,
    verdict: input.verdict,
  });
}

export async function clearBatchDecisions(findingIds: string[]): Promise<ReviewDraftView> {
  return postReviewMutation({ action: "clear-decision", targetIds: findingIds });
}

export async function undoFinding(findingId: string): Promise<ReviewDraftView> {
  return postReviewMutation({
    action: "undo",
    targetIds: [findingId],
  });
}

export async function updateFindingNotes(
  findingId: string,
  notes: string,
): Promise<ReviewDraftView> {
  return postReviewMutation({
    action: "update-notes",
    targetIds: [findingId],
    notes,
  });
}

export async function updateFindingScope(input: {
  findingId: string;
  scope: DecisionScope;
  reason?: string;
}): Promise<ReviewDraftView> {
  return postReviewMutation({
    action: "update-scope",
    targetIds: [input.findingId],
    scope: input.scope,
    ...(input.reason === undefined ? {} : { reason: input.reason }),
  });
}

export async function revokeAcceptedDecision(decisionId: string): Promise<void> {
  const response = await fetch("/api/accepted", {
    method: "POST",
    headers: authHeaders(),
    body: JSON.stringify({ action: "revoke", decisionId }),
  });
  if (!response.ok) {
    const payload = (await response.json().catch(() => ({}))) as { message?: string };
    throw new Error(payload.message ?? `accepted finding API failed: ${String(response.status)}`);
  }
}

export async function finalizeReview(): Promise<FinalizationResultView> {
  const response = await fetch("/api/finalize", {
    method: "POST",
    headers: authHeaders(),
  });
  const payload = (await response.json()) as { result?: FinalizationResultView; message?: string };
  if (!response.ok || payload.result === undefined) {
    throw new Error(payload.message ?? `failed to finalize review: ${String(response.status)}`);
  }
  return payload.result;
}
