import type { Doc } from "@code/backend/convex/_generated/dataModel";
import type { ProjectIssueStatus } from "@code/primitives/project-issue";
import {
  getProjectWorkNextAction,
  getProjectWorkProgress,
  getProjectWorkStatus,
} from "@code/primitives/project-work";
import type { ProjectVerificationStatus } from "@code/primitives/project-work";

type ProjectIssue = Doc<"projectIssues">;
type ProjectArtifact = Doc<"projectArtifacts">;

export interface ProjectSourceSummary {
  readonly host: string;
  readonly repositoryPath: string;
  readonly url: string;
}

export interface ProjectIssueSummary {
  readonly active: number;
  readonly completed: number;
  readonly needsInput: number;
  readonly total: number;
}

export interface ProjectVerificationCheck {
  readonly detail: string;
  readonly label: string;
  readonly state: "attention" | "complete" | "current" | "upcoming";
}

export interface ProjectActivityItem {
  readonly detail: string;
  readonly label: string;
  readonly time: string;
}

export interface ProjectPullRequest {
  readonly number: number | undefined;
  readonly reviewUrl: string | undefined;
  readonly state: "available" | "pending";
}

export interface ProjectLoopView {
  readonly activity: readonly ProjectActivityItem[];
  readonly currentState: string;
  readonly nextAction: string;
  readonly progress: number;
  readonly pullRequest: ProjectPullRequest;
  readonly status: ReturnType<typeof getProjectWorkStatus>;
  readonly verification: ProjectVerificationStatus;
  readonly verificationChecks: readonly ProjectVerificationCheck[];
  readonly verificationNote: string;
}

const STATUS_TEXT: Readonly<Record<ProjectIssueStatus, string>> = {
  completed:
    "Implementation and verification are complete. Review the resulting change.",
  failed:
    "The run stopped with its workspace preserved. Review the failure and retry when ready.",
  "needs-input":
    "The run is paused until a project decision or missing piece is resolved.",
  open: "The issue is ready to start. Zopu will keep the project context attached to the run.",
  queued: "The issue is queued for the project manager to pick up.",
  working:
    "The project manager is working through the issue and recording durable evidence.",
};

const formatTime = (timestamp: number): string =>
  new Intl.DateTimeFormat(undefined, {
    hour: "numeric",
    minute: "2-digit",
  }).format(timestamp);

const relativeTime = (timestamp: number): string => {
  const minutes = Math.max(0, Math.round((Date.now() - timestamp) / 60_000));
  if (minutes < 1) {
    return "just now";
  }
  if (minutes < 60) {
    return `${minutes}m ago`;
  }
  const hours = Math.round(minutes / 60);
  if (hours < 24) {
    return `${hours}h ago`;
  }
  return `${Math.round(hours / 24)}d ago`;
};

const allArtifactText = (artifacts: readonly ProjectArtifact[]): string =>
  artifacts.map((artifact) => artifact.content).join("\n");

const extractPullRequest = (
  artifacts: readonly ProjectArtifact[],
  source: ProjectSourceSummary | undefined
): ProjectPullRequest => {
  const text = allArtifactText(artifacts);
  const urlMatch = text.match(/https?:\/\/[^\s)]+\/pulls\/\d+/iu)?.[0];
  const numberMatch = text.match(/\bPR\s*#(?<number>\d+)\b/iu);
  const numberValue = numberMatch?.groups?.number;
  const number = numberValue ? Number(numberValue) : undefined;
  let reviewUrl = urlMatch;
  if (!reviewUrl && number && source) {
    reviewUrl = `${source.url.replace(/\/$/u, "")}/pulls/${number}`;
  }

  return {
    number,
    reviewUrl,
    state: reviewUrl ? "available" : "pending",
  };
};

const extractVerificationNote = (
  artifacts: readonly ProjectArtifact[],
  status: ProjectIssueStatus
): string => {
  const evidenceLine = allArtifactText(artifacts)
    .split("\n")
    .map((line) => line.trim())
    .find((line) => /verification|tests?|browser|passed|failed/iu.test(line));

  if (evidenceLine) {
    return evidenceLine.replace(/^[-*#\s]+/u, "").slice(0, 140);
  }
  if (status === "completed") {
    return "The agent reported completion after its verification step.";
  }
  if (status === "failed") {
    return "Verification did not complete successfully.";
  }
  if (status === "working") {
    return "Verification will appear here as the run records evidence.";
  }
  return "No verification evidence has been recorded yet.";
};

const makeRunCheck = (status: ProjectIssueStatus): ProjectVerificationCheck => {
  if (status === "queued") {
    return {
      detail: "Agent admission is queued",
      label: "Agent run",
      state: "current",
    };
  }
  if (status === "open") {
    return {
      detail: "Start the issue to create a run",
      label: "Agent run",
      state: "upcoming",
    };
  }
  return {
    detail: "Project manager run is attached",
    label: "Agent run",
    state: "complete",
  };
};

const makeVerificationCheck = (
  verification: ProjectVerificationStatus
): ProjectVerificationCheck => {
  if (verification === "passed") {
    return {
      detail: "Verification passed",
      label: "Verification",
      state: "complete",
    };
  }
  if (verification === "failed") {
    return {
      detail: "Review the preserved failure",
      label: "Verification",
      state: "attention",
    };
  }
  if (verification === "blocked") {
    return {
      detail: "Waiting on a project decision",
      label: "Verification",
      state: "attention",
    };
  }
  if (verification === "running") {
    return {
      detail: "Evidence is being recorded",
      label: "Verification",
      state: "current",
    };
  }
  return {
    detail: "No verification result yet",
    label: "Verification",
    state: "upcoming",
  };
};

const makeChecks = (
  status: ProjectIssueStatus,
  verification: ProjectVerificationStatus,
  pullRequest: ProjectPullRequest
): readonly ProjectVerificationCheck[] => {
  const issueCheck: ProjectVerificationCheck =
    status === "open"
      ? {
          detail: "Waiting to be started",
          label: "Issue accepted",
          state: "upcoming",
        }
      : {
          detail: "Issue context is attached",
          label: "Issue accepted",
          state: "complete",
        };
  const pullRequestCheck: ProjectVerificationCheck =
    pullRequest.state === "available"
      ? {
          detail: "Ready for human review",
          label: "Gitea pull request",
          state: "complete",
        }
      : {
          detail: "Appears after a verified run publishes one",
          label: "Gitea pull request",
          state: "upcoming",
        };
  return [
    issueCheck,
    makeRunCheck(status),
    makeVerificationCheck(verification),
    pullRequestCheck,
  ];
};

const buildActivity = (
  issue: ProjectIssue,
  artifacts: readonly ProjectArtifact[],
  statusLabel: string
): readonly ProjectActivityItem[] => {
  const artifact = [...artifacts]
    .toSorted((left, right) => right.updatedAt - left.updatedAt)
    .find((candidate) => candidate.path !== "work.md");
  const items: ProjectActivityItem[] = [
    {
      detail: `Issue #${issue.number} entered the project loop`,
      label: "Issue created",
      time: formatTime(issue.createdAt),
    },
  ];

  if (issue.updatedAt !== issue.createdAt) {
    items.unshift({
      detail: `Status is now ${statusLabel.toLowerCase()}`,
      label: "Work state updated",
      time: relativeTime(issue.updatedAt),
    });
  }

  if (artifact) {
    items.unshift({
      detail: `Durable project evidence changed in ${artifact.path}`,
      label: "Evidence recorded",
      time: relativeTime(artifact.updatedAt),
    });
  }

  return items.slice(0, 3);
};

export const summarizeProjectIssues = (
  issues: readonly ProjectIssue[] | undefined
): ProjectIssueSummary => {
  if (!issues) {
    return { active: 0, completed: 0, needsInput: 0, total: 0 };
  }
  return {
    active: issues.filter(
      (issue) => !["completed", "failed"].includes(issue.status)
    ).length,
    completed: issues.filter((issue) => issue.status === "completed").length,
    needsInput: issues.filter((issue) => issue.status === "needs-input").length,
    total: issues.length,
  };
};

export const buildProjectLoopView = ({
  artifacts,
  issue,
  source,
}: {
  readonly artifacts: readonly ProjectArtifact[] | undefined;
  readonly issue: ProjectIssue | undefined;
  readonly source: ProjectSourceSummary | undefined;
}): ProjectLoopView | null => {
  if (!issue) {
    return null;
  }

  const status = getProjectWorkStatus(issue.status);
  const { verification } = status;
  const resolvedArtifacts = artifacts ?? [];
  const pullRequest = extractPullRequest(resolvedArtifacts, source);

  return {
    activity: buildActivity(issue, resolvedArtifacts, status.label),
    currentState: STATUS_TEXT[issue.status],
    nextAction: getProjectWorkNextAction(issue.status),
    progress: getProjectWorkProgress(issue.status),
    pullRequest,
    status,
    verification,
    verificationChecks: makeChecks(issue.status, verification, pullRequest),
    verificationNote: extractVerificationNote(resolvedArtifacts, issue.status),
  };
};
