import { api } from "@code/backend/convex/_generated/api";
import type { Id } from "@code/backend/convex/_generated/dataModel";
import { useFlueClient } from "@flue/react";
import { useAction, useMutation, useQuery } from "convex/react";
import { useState } from "react";

import {
  buildProjectLoopView,
  summarizeProjectIssues,
} from "@/lib/projects/project-evidence";

const errorMessage = (error: unknown) =>
  error instanceof Error ? error.message : String(error);

export const useProjectWorkspace = () => {
  const flueClient = useFlueClient();
  const projects = useQuery(api.projects.list);
  const [selectedProjectId, setSelectedProjectId] =
    useState<Id<"projects"> | null>(null);
  const [repository, setRepository] = useState("");
  const [issueTitle, setIssueTitle] = useState("");
  const [issueBody, setIssueBody] = useState("");
  const [selectedIssueId, setSelectedIssueId] =
    useState<Id<"projectIssues"> | null>(null);
  const [pendingAction, setPendingAction] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const importPublicGit = useAction(api.projects.importPublicGit);
  const createIssue = useMutation(api.projectIssues.create);
  const createIssueFromSignal = useMutation(api.projectIssues.createFromSignal);
  const beginIssue = useMutation(api.projectIssues.begin);
  const markDispatchFailed = useMutation(api.projectIssues.markDispatchFailed);
  const activeProjectId =
    selectedProjectId ??
    (projects?.[0]?.id as unknown as Id<"projects">) ??
    null;
  const artifacts = useQuery(
    api.projectArtifacts.list,
    activeProjectId ? { projectId: activeProjectId } : "skip"
  );
  const issues = useQuery(
    api.projectIssues.list,
    activeProjectId ? { projectId: activeProjectId } : "skip"
  );
  const events = useQuery(
    api.projectIssues.events,
    activeProjectId ? { projectId: activeProjectId } : "skip"
  );
  const pullRequest = events
    ?.map((event) => event.data)
    .find(
      (
        data
      ): data is {
        pullRequest: {
          baseBranch: string;
          branch: string;
          number: number;
          status: "open" | "closed" | "merged";
          url: string;
        };
      } =>
        typeof data === "object" &&
        data !== null &&
        "pullRequest" in data &&
        typeof data.pullRequest === "object" &&
        data.pullRequest !== null
    )?.pullRequest;

  const connectRepository = async () => {
    setPendingAction("connect");
    setError(null);
    try {
      const outcome = await importPublicGit({ repositoryUrl: repository });
      setSelectedProjectId(outcome.id as unknown as Id<"projects">);
      setRepository("");
    } catch (caughtError) {
      setError(errorMessage(caughtError));
    } finally {
      setPendingAction(null);
    }
  };

  const raiseIssue = async (input?: {
    readonly body?: string;
    readonly title?: string;
  }) => {
    if (!activeProjectId) {
      return;
    }
    const nextTitle = input?.title ?? issueTitle;
    const nextBody = input?.body ?? issueBody;
    setPendingAction("issue");
    setError(null);
    try {
      const issueId = await createIssue({
        body: nextBody,
        projectId: activeProjectId,
        title: nextTitle,
      });
      setSelectedIssueId(issueId);
      setIssueTitle("");
      setIssueBody("");
    } catch (caughtError) {
      setError(errorMessage(caughtError));
    } finally {
      setPendingAction(null);
    }
  };

  const raiseIssueFromSignal = async (signalId: Id<"signals">) => {
    setPendingAction(`signal:${signalId}`);
    setError(null);
    try {
      const outcome = await createIssueFromSignal({ signalId });
      setSelectedIssueId(outcome.issueId);
    } catch (caughtError) {
      setError(errorMessage(caughtError));
    } finally {
      setPendingAction(null);
    }
  };

  const startIssue = async (
    issueId: Id<"projectIssues">,
    issueNumber: number,
    title: string
  ) => {
    const actionKey = `issue:${issueId}`;
    setPendingAction(actionKey);
    setError(null);
    try {
      await beginIssue({ issueId });
      await flueClient.agents.send("project-manager", String(issueId), {
        message: `Start project issue ${issueNumber}: ${title}. Read the bound context and complete the workflow.`,
      });
    } catch (caughtError) {
      const message = errorMessage(caughtError);
      await markDispatchFailed({ error: message, issueId });
      setError(message);
    } finally {
      setPendingAction(null);
    }
  };

  const startWorkUnit = async (issueId: Id<"projectIssues">) => {
    const issue = issues?.find((candidate) => candidate._id === issueId);
    if (!issue) {
      setError("That work unit is no longer available");
      return;
    }
    await startIssue(issue._id, issue.number, issue.title);
  };
  const selectedProject =
    projects?.find(
      (project) => project.id === (activeProjectId as unknown as string)
    ) ?? null;
  const selectedIssue =
    issues?.find((issue) => issue._id === selectedIssueId) ?? issues?.[0];
  const projectLoop = buildProjectLoopView({
    artifacts,
    issue: selectedIssue,
    source: selectedProject?.sources[0],
  });
  const issueSummary = summarizeProjectIssues(issues);

  return {
    artifacts,
    connectRepository,
    error,
    events,
    issueBody,
    issueSummary,
    issueTitle,
    issues,
    pendingAction,
    projectLoop,
    projects,
    pullRequest,
    raiseIssue,
    raiseIssueFromSignal,
    repository,
    selectedIssue,
    selectedIssueId,
    selectedProject,
    selectedProjectId: activeProjectId,
    setIssueBody,
    setIssueTitle,
    setRepository,
    setSelectedIssueId,
    setSelectedProjectId,
    startIssue,
    startWorkUnit,
  } as const;
};
