import type { Doc, Id } from "@code/backend/convex/_generated/dataModel";
import { Button } from "@code/ui/components/button";
import { Input } from "@code/ui/components/input";
import { Textarea } from "@code/ui/components/textarea";
import {
  Bot,
  ExternalLink,
  FileText,
  GitFork,
  GitPullRequest,
  Play,
  Plus,
} from "lucide-react";

import UserMenu from "@/components/user-menu";
import { useProjectWorkspace } from "@/hooks/use-project-workspace";

const statusStyle: Record<Doc<"projectIssues">["status"], string> = {
  completed:
    "border-emerald-500/40 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
  failed: "border-destructive/40 bg-destructive/10 text-destructive",
  "needs-input":
    "border-orange-500/40 bg-orange-500/10 text-orange-700 dark:text-orange-300",
  open: "border-border text-muted-foreground",
  queued:
    "border-amber-500/40 bg-amber-500/10 text-amber-700 dark:text-amber-300",
  working: "border-blue-500/40 bg-blue-500/10 text-blue-700 dark:text-blue-300",
};

interface RepositoryFormProps {
  readonly busy: boolean;
  readonly onChange: (value: string) => void;
  readonly onSubmit: () => Promise<void>;
  readonly value: string;
}

const RepositoryForm = ({
  busy,
  onChange,
  onSubmit,
  value,
}: RepositoryFormProps) => (
  <form
    className="grid gap-3 border-b p-4"
    onSubmit={(event) => {
      event.preventDefault();
      void onSubmit();
    }}
  >
    <div className="space-y-1">
      <label className="text-xs font-medium" htmlFor="github-repository">
        Public GitHub repository
      </label>
      <p className="text-xs text-muted-foreground">
        Connect with an owner/name slug or repository URL.
      </p>
    </div>
    <Input
      id="github-repository"
      onChange={(event) => onChange(event.target.value)}
      placeholder="rivet-dev/rivet"
      required
      value={value}
    />
    <Button disabled={busy || value.trim().length === 0} type="submit">
      <GitFork data-icon="inline-start" />
      {busy ? "Connecting" : "Connect repository"}
    </Button>
  </form>
);

interface ProjectListProps {
  readonly projects: readonly Doc<"projects">[] | undefined;
  readonly selectedId: Id<"projects"> | null;
  readonly onSelect: (projectId: Id<"projects">) => void;
}

const ProjectList = ({ projects, selectedId, onSelect }: ProjectListProps) => {
  const renderContent = () => {
    if (projects === undefined) {
      return (
        <p className="p-3 text-xs text-muted-foreground">Loading projects...</p>
      );
    }
    if (projects.length === 0) {
      return (
        <p className="p-3 text-xs leading-relaxed text-muted-foreground">
          Connect a repository to create its project artifacts and issue queue.
        </p>
      );
    }
    return (
      <div className="grid gap-1">
        {projects.map((project) => (
          <button
            className={`w-full border px-3 py-2 text-left transition-colors ${
              selectedId === project._id
                ? "border-foreground/30 bg-muted"
                : "border-transparent hover:bg-muted/60"
            }`}
            key={project._id}
            onClick={() => onSelect(project._id)}
            type="button"
          >
            <span className="block truncate text-sm font-medium">
              {project.name}
            </span>
            <span className="block truncate text-xs text-muted-foreground">
              {project.repoOwner}/{project.repoName}
            </span>
          </button>
        ))}
      </div>
    );
  };

  return (
    <nav
      aria-label="Connected projects"
      className="min-h-0 flex-1 overflow-y-auto p-2"
    >
      {renderContent()}
    </nav>
  );
};

interface ArtifactGridProps {
  readonly artifacts: readonly Doc<"projectArtifacts">[] | undefined;
}

const ArtifactGrid = ({ artifacts }: ArtifactGridProps) => (
  <section aria-labelledby="artifact-heading" className="space-y-3">
    <div>
      <h2 className="text-lg font-semibold" id="artifact-heading">
        Project artifacts
      </h2>
      <p className="text-sm text-muted-foreground">
        Canonical context staged into every issue workspace.
      </p>
    </div>
    {artifacts === undefined ? (
      <p className="text-sm text-muted-foreground">Loading artifacts...</p>
    ) : (
      <div className="grid gap-2 md:grid-cols-2 xl:grid-cols-3">
        {artifacts.map((artifact) => (
          <article className="min-w-0 border bg-card p-3" key={artifact._id}>
            <div className="mb-2 flex items-center justify-between gap-3">
              <div className="flex min-w-0 items-center gap-2">
                <FileText className="size-4 shrink-0 text-muted-foreground" />
                <h3 className="truncate font-mono text-xs font-medium">
                  {artifact.path}
                </h3>
              </div>
              <span className="shrink-0 text-[11px] text-muted-foreground">
                r{artifact.revision}
              </span>
            </div>
            <p className="line-clamp-4 whitespace-pre-wrap text-xs leading-relaxed text-muted-foreground">
              {artifact.content}
            </p>
          </article>
        ))}
      </div>
    )}
  </section>
);

interface IssueComposerProps {
  readonly body: string;
  readonly busy: boolean;
  readonly onBodyChange: (value: string) => void;
  readonly onSubmit: () => Promise<void>;
  readonly onTitleChange: (value: string) => void;
  readonly title: string;
}

const IssueComposer = ({
  body,
  busy,
  onBodyChange,
  onSubmit,
  onTitleChange,
  title,
}: IssueComposerProps) => (
  <form
    className="grid gap-3 border p-4"
    onSubmit={(event) => {
      event.preventDefault();
      void onSubmit();
    }}
  >
    <div>
      <h2 className="font-semibold">Raise an issue</h2>
      <p className="text-xs text-muted-foreground">
        The issue becomes the identity for one Flue agent and AgentOS workspace.
      </p>
    </div>
    <div className="space-y-1.5">
      <label className="text-xs font-medium" htmlFor="issue-title">
        Title
      </label>
      <Input
        id="issue-title"
        maxLength={160}
        onChange={(event) => onTitleChange(event.target.value)}
        required
        value={title}
      />
    </div>
    <div className="space-y-1.5">
      <label className="text-xs font-medium" htmlFor="issue-body">
        Description
      </label>
      <Textarea
        id="issue-body"
        maxLength={10_000}
        onChange={(event) => onBodyChange(event.target.value)}
        required
        rows={5}
        value={body}
      />
    </div>
    <Button
      disabled={busy || title.trim().length < 3 || body.trim().length < 10}
      type="submit"
    >
      <Plus data-icon="inline-start" />
      {busy ? "Creating" : "Create issue"}
    </Button>
  </form>
);

interface IssueListProps {
  readonly issues: readonly Doc<"projectIssues">[] | undefined;
  readonly pendingAction: string | null;
  readonly onStart: (
    issueId: Id<"projectIssues">,
    issueNumber: number,
    title: string
  ) => Promise<void>;
}

const IssueList = ({ issues, pendingAction, onStart }: IssueListProps) => {
  const renderContent = () => {
    if (issues === undefined) {
      return <p className="text-sm text-muted-foreground">Loading issues...</p>;
    }
    if (issues.length === 0) {
      return (
        <p className="border p-4 text-sm text-muted-foreground">
          No issues yet. Describe a concrete change to start the workflow.
        </p>
      );
    }
    return (
      <div className="grid gap-2">
        {issues.map((issue) => {
          const canStart = ["open", "needs-input", "failed"].includes(
            issue.status
          );
          const busy = pendingAction === `issue:${issue._id}`;
          let actionLabel: string = issue.status;
          if (canStart) {
            actionLabel = "Start agent";
          }
          if (busy) {
            actionLabel = "Dispatching";
          }
          return (
            <article
              className="grid gap-3 border bg-card p-4 md:grid-cols-[1fr_auto] md:items-center"
              key={issue._id}
            >
              <div className="min-w-0">
                <div className="mb-1 flex flex-wrap items-center gap-2">
                  <span className="font-mono text-xs text-muted-foreground">
                    #{issue.number}
                  </span>
                  <span
                    className={`border px-1.5 py-0.5 text-[11px] font-medium ${statusStyle[issue.status]}`}
                  >
                    {issue.status}
                  </span>
                </div>
                <h3 className="font-medium">{issue.title}</h3>
                <p className="mt-1 line-clamp-2 text-sm text-muted-foreground">
                  {issue.body}
                </p>
              </div>
              <Button
                disabled={!canStart || busy}
                onClick={() =>
                  void onStart(issue._id, issue.number, issue.title)
                }
                size="sm"
                type="button"
                variant={canStart ? "default" : "outline"}
              >
                {canStart ? (
                  <Play data-icon="inline-start" />
                ) : (
                  <Bot data-icon="inline-start" />
                )}
                {actionLabel}
              </Button>
            </article>
          );
        })}
      </div>
    );
  };

  return (
    <section aria-labelledby="issue-heading" className="space-y-3">
      <div className="flex items-center gap-2">
        <GitPullRequest className="size-4 text-muted-foreground" />
        <h2 className="text-lg font-semibold" id="issue-heading">
          Issue queue
        </h2>
      </div>
      {renderContent()}
    </section>
  );
};

export const ProjectWorkspacePage = () => {
  const workspace = useProjectWorkspace();
  const handleRepositoryChange = workspace.setRepository;
  const handleRepositorySubmit = workspace.connectRepository;
  const handleProjectSelect = workspace.setSelectedProjectId;
  const handleIssueBodyChange = workspace.setIssueBody;
  const handleIssueSubmit = workspace.raiseIssue;
  const handleIssueTitleChange = workspace.setIssueTitle;
  const handleIssueStart = workspace.startIssue;

  return (
    <main className="min-h-svh bg-background text-foreground">
      <header className="border-b">
        <div className="mx-auto flex max-w-[1400px] items-center justify-between px-4 py-3 md:px-6">
          <div className="flex items-center gap-3">
            <div className="grid size-8 place-items-center border bg-muted">
              <Bot className="size-4" />
            </div>
            <div>
              <p className="text-sm font-semibold">Project manager</p>
              <p className="text-xs text-muted-foreground">
                GitHub, Flue, AgentOS
              </p>
            </div>
          </div>
          <UserMenu />
        </div>
      </header>

      <div className="mx-auto grid min-h-[calc(100svh-57px)] max-w-[1400px] md:grid-cols-[260px_1fr]">
        <aside className="flex min-h-0 flex-col border-b md:border-r md:border-b-0">
          <RepositoryForm
            busy={workspace.pendingAction === "connect"}
            onChange={handleRepositoryChange}
            onSubmit={handleRepositorySubmit}
            value={workspace.repository}
          />
          <ProjectList
            onSelect={handleProjectSelect}
            projects={workspace.projects}
            selectedId={workspace.selectedProjectId}
          />
        </aside>

        <div className="min-w-0 p-4 md:p-6 lg:p-8">
          {workspace.error ? (
            <div className="mb-4 border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive">
              {workspace.error}
            </div>
          ) : null}

          {workspace.selectedProject ? (
            <div className="grid gap-8">
              <div className="flex flex-col gap-3 border-b pb-5 sm:flex-row sm:items-end sm:justify-between">
                <div>
                  <p className="text-xs text-muted-foreground">
                    Connected GitHub project
                  </p>
                  <h1 className="text-2xl font-semibold tracking-tight">
                    {workspace.selectedProject.repoOwner}/
                    {workspace.selectedProject.repoName}
                  </h1>
                  <p className="mt-1 max-w-2xl text-sm text-muted-foreground">
                    {workspace.selectedProject.description ??
                      "No repository description is available."}
                  </p>
                </div>
                <Button
                  onClick={() =>
                    window.open(
                      workspace.selectedProject?.repoUrl,
                      "_blank",
                      "noopener,noreferrer"
                    )
                  }
                  type="button"
                  variant="outline"
                >
                  <ExternalLink data-icon="inline-start" />
                  Open GitHub
                </Button>
              </div>

              <ArtifactGrid artifacts={workspace.artifacts} />

              <div className="grid items-start gap-5 lg:grid-cols-[minmax(280px,380px)_1fr]">
                <IssueComposer
                  body={workspace.issueBody}
                  busy={workspace.pendingAction === "issue"}
                  onBodyChange={handleIssueBodyChange}
                  onSubmit={handleIssueSubmit}
                  onTitleChange={handleIssueTitleChange}
                  title={workspace.issueTitle}
                />
                <IssueList
                  issues={workspace.issues}
                  onStart={handleIssueStart}
                  pendingAction={workspace.pendingAction}
                />
              </div>
            </div>
          ) : (
            <div className="grid min-h-[60svh] place-items-center text-center">
              <div className="max-w-sm space-y-2">
                <GitFork className="mx-auto size-7 text-muted-foreground" />
                <h1 className="text-xl font-semibold">
                  Connect your first repository
                </h1>
                <p className="text-sm text-muted-foreground">
                  Zopu creates the project artifact set and opens an issue queue
                  for agent work.
                </p>
              </div>
            </div>
          )}
        </div>
      </div>
    </main>
  );
};
