import type { api } from "@code/backend/convex/_generated/api";
import type { Doc } from "@code/backend/convex/_generated/dataModel";
import {
  getProjectWorkNextAction,
  getProjectWorkProgress,
  getProjectWorkStatus,
} from "@code/primitives/project-work";
import type {
  MobileAssistantView,
  MobileProjectView,
  MobileSignalView,
  MobileWorkspaceView,
  MobileWorkUnitTone,
  MobileWorkUnitView,
} from "@code/ui/components/mobile-product";
import type { FunctionReturnType } from "convex/server";

type SignalList = FunctionReturnType<typeof api.signals.list>;
type ProjectEventList = FunctionReturnType<typeof api.projectIssues.events>;
type ProjectView = FunctionReturnType<typeof api.projects.list>[number];

interface BuildMobileWorkspaceViewInput {
  readonly artifacts: readonly Doc<"projectArtifacts">[] | undefined;
  readonly assistant: MobileAssistantView;
  readonly events: ProjectEventList | undefined;
  readonly issues: readonly Doc<"projectIssues">[] | undefined;
  readonly organizationLabel?: string;
  readonly projects: readonly ProjectView[] | undefined;
  readonly selectedProject: ProjectView | null;
  readonly selectedWorkUnitId?: string;
  readonly signals: SignalList | undefined;
}

const updatedDateFormatter = new Intl.DateTimeFormat("en", {
  day: "numeric",
  month: "short",
});

const statusTone = (
  status: ReturnType<typeof getProjectWorkStatus>
): MobileWorkUnitTone => {
  if (status.phase === "completed") {
    return "green";
  }
  if (status.phase === "failed" || status.phase === "waiting") {
    return "orange";
  }
  if (status.phase === "captured") {
    return "purple";
  }
  return "blue";
};

const isPullRequestData = (
  value: unknown
): value is {
  pullRequest: {
    number: number;
    status: "open" | "closed" | "merged";
    url: string;
  };
} =>
  typeof value === "object" &&
  value !== null &&
  "pullRequest" in value &&
  typeof value.pullRequest === "object" &&
  value.pullRequest !== null &&
  "number" in value.pullRequest &&
  typeof value.pullRequest.number === "number" &&
  "url" in value.pullRequest &&
  typeof value.pullRequest.url === "string";

const toWorkUnit = (
  issue: Doc<"projectIssues">,
  artifactCount: number,
  events: ProjectEventList | undefined
): MobileWorkUnitView => {
  const status = getProjectWorkStatus(issue.status);
  const pullRequest = events
    ?.filter((event) => event.issueId === issue._id)
    .map((event) => event.data)
    .find(isPullRequestData)?.pullRequest;
  return {
    artifactCount,
    canRetry: status.phase === "failed" || status.phase === "waiting",
    canStart: status.phase === "captured",
    code: `#${issue.number}`,
    id: issue._id,
    nextAction: getProjectWorkNextAction(issue.status),
    progress: getProjectWorkProgress(issue.status),
    pullRequestNumber: pullRequest?.number,
    reviewUrl: pullRequest?.url,
    statusLabel: status.label,
    summary: issue.body,
    title: issue.title,
    tone: statusTone(status),
    updatedLabel: updatedDateFormatter.format(issue.updatedAt),
  };
};

const toLatestSignalView = (
  signal: SignalList[number]["signal"] | undefined
): MobileSignalView | undefined => {
  if (!signal) {
    return;
  }
  return {
    desiredOutcome: signal.problemStatement.desiredOutcome,
    id: signal._id,
    projectId: signal.projectId ?? undefined,
    summary: signal.problemStatement.summary,
    title: signal.problemStatement.title,
  };
};

const relevantSignalsForProject = (
  signals: SignalList | undefined,
  projectId: ProjectView["id"] | undefined
) =>
  signals?.filter(
    ({ signal }) =>
      !signal.projectId || String(signal.projectId) === String(projectId)
  ) ?? [];

const makeProjectViews = (
  projects: readonly ProjectView[] | undefined
): readonly MobileProjectView[] =>
  projects?.map((project) => ({
    connected: project.sources.length > 0,
    id: project.id,
    name: project.name,
  })) ?? [];

export const buildMobileWorkspaceView = ({
  artifacts,
  assistant,
  events,
  issues,
  organizationLabel = "Personal workspace",
  projects,
  selectedProject,
  selectedWorkUnitId,
  signals,
}: BuildMobileWorkspaceViewInput): MobileWorkspaceView => {
  const artifactCount = artifacts?.length ?? 0;
  const workUnits =
    issues?.map((issue) => toWorkUnit(issue, artifactCount, events)) ?? [];
  const selectedWorkUnit =
    workUnits.find((workUnit) => workUnit.id === selectedWorkUnitId) ??
    workUnits.find((workUnit) => workUnit.tone === "blue") ??
    workUnits[0];
  const selectedProjectId = selectedProject?.id;
  const relevantSignals = relevantSignalsForProject(signals, selectedProjectId);
  const latestSignal = relevantSignals[0]?.signal;
  const activeCount = workUnits.filter(
    (workUnit) => workUnit.progress < 100
  ).length;
  const needsAttentionCount = issues?.filter(
    (issue) => issue.status === "failed" || issue.status === "needs-input"
  ).length;
  const shippedCount = issues?.filter(
    (issue) => issue.status === "completed"
  ).length;
  const hasSelectedProject = Boolean(selectedProject);
  const projectViews = makeProjectViews(projects);

  return {
    activeCount,
    activityCount: workUnits.length + relevantSignals.length,
    artifactCount,
    assistant,
    isLoading:
      projects === undefined ||
      (hasSelectedProject && (artifacts === undefined || issues === undefined)),
    latestSignal: toLatestSignalView(latestSignal),
    needsAttentionCount: needsAttentionCount ?? 0,
    organizationLabel,
    projectName: selectedProject?.name,
    projects: projectViews,
    selectedProjectId: selectedProject?.id,
    selectedWorkUnit,
    shippedCount: shippedCount ?? 0,
    totalCount: workUnits.length,
    workUnits,
  };
};
