import { useMemo, useState } from "react";
import { useParams } from "react-router";
import type { WorkflowSettings } from "@qltysh/fabro-api-client";
import { CollapsibleFile } from "../components/collapsible-file";
import { StageSidebar } from "../components/stage-sidebar";
import {
  Badge,
  Count,
  Mono,
  Muted,
  Panel,
  PanelSkeleton,
  Row,
  type SettingsView,
  Toggle,
  ViewToggle,
} from "../components/settings-panel";
import { useRunSettings, useRunStages } from "../lib/queries";
import { mapRunStagesToSidebarStages } from "../lib/stage-sidebar";
import {
  type UnknownRecord,
  getArray,
  getBool,
  getObject,
  getString,
  objectKeyCount,
} from "../lib/run-settings-snapshot";

export const handle = { wide: true };

export default function RunSettingsPage() {
  const { id } = useParams();
  const stagesQuery = useRunStages(id);
  const settingsQuery = useRunSettings<WorkflowSettings>(id);
  const stages = useMemo(
    () => mapRunStagesToSidebarStages(stagesQuery.data),
    [stagesQuery.data],
  );
  const [view, setView] = useState<SettingsView>("settings");
  const snapshot = settingsQuery.data;

  return (
    <div className="flex gap-6">
      <StageSidebar stages={stages} runId={id!} activeLink="settings" />

      <div className="min-w-0 flex-1 space-y-6">
        <PageIntro view={view} setView={setView} />

        {!snapshot ? (
          <>
            <PanelSkeleton />
            <PanelSkeleton />
            <PanelSkeleton />
            <PanelSkeleton />
          </>
        ) : view === "settings" ? (
          <>
            <WorkflowPanel snapshot={snapshot} />
            <EnvironmentPanel snapshot={snapshot} />
            <GitPanel snapshot={snapshot} />
            <ArtifactsPanel snapshot={snapshot} />
          </>
        ) : (
          <CollapsibleFile
            file={{
              name: "settings.json",
              contents: JSON.stringify(snapshot, null, 2),
              lang: "json",
            }}
          />
        )}
      </div>
    </div>
  );
}

function PageIntro({
  view,
  setView,
}: {
  view: SettingsView;
  setView: (v: SettingsView) => void;
}) {
  return (
    <div className="flex items-start justify-between gap-6">
      <div>
        <h2 className="text-base font-semibold text-fg">Run settings</h2>
        <p className="mt-1 text-sm/6 text-fg-3">
          Frozen settings snapshot used by this run.
        </p>
      </div>
      <ViewToggle view={view} setView={setView} />
    </div>
  );
}

function WorkflowPanel({ snapshot }: { snapshot: WorkflowSettings }) {
  const workflow = getObject(snapshot, "workflow");
  const run = getObject(snapshot, "run");
  const name = getString(workflow, "name");
  const description = getString(workflow, "description");
  const goal = getObject(run, "goal");
  return (
    <Panel title="Workflow">
      <Row title="Name" help="Workflow identifier used by this run.">
        {name ? <Mono>{name}</Mono> : <Muted>Unnamed</Muted>}
      </Row>
      {description ? (
        <Row title="Description">
          <span className="text-fg-2">{description}</span>
        </Row>
      ) : null}
      <Row title="Goal" help="Goal text or file used for this run.">
        <GoalValue goal={goal} />
      </Row>
      <Row title="Inputs" help="Run input variables.">
        <Count
          n={objectKeyCount(run, "inputs")}
          singular="value"
          plural="values"
        />
      </Row>
      <Row title="Metadata" help="Combined run / workflow / project labels.">
        <Count
          n={
            objectKeyCount(run, "metadata") +
            objectKeyCount(workflow, "metadata") +
            objectKeyCount(getObject(snapshot, "project"), "metadata")
          }
          singular="label"
          plural="labels"
        />
      </Row>
    </Panel>
  );
}

function EnvironmentPanel({ snapshot }: { snapshot: WorkflowSettings }) {
  const environment = getObject(getObject(snapshot, "run"), "environment");
  const provider = getString(environment, "provider");
  const environmentId = getString(environment, "id");
  const image = getObject(environment, "image");
  const imageRef = getString(image, "ref");
  const lifecycle = getObject(environment, "lifecycle");
  return (
    <Panel title="Environment">
      <Row title="Name" help="Named environment selected for this run.">
        {environmentId ? <Mono>{environmentId}</Mono> : <Muted>Default</Muted>}
      </Row>
      <Row title="Provider" help="Sandbox provider for this environment.">
        {provider ? <Badge>{provider}</Badge> : <Muted>Unknown</Muted>}
      </Row>
      {imageRef ? (
        <Row title="Image" help="Image or snapshot used for this environment.">
          <Mono>{imageRef}</Mono>
        </Row>
      ) : null}
      <Row title="Preserve" help="Keep the sandbox after the run completes.">
        <Toggle on={getBool(lifecycle, "preserve") ?? false} />
      </Row>
      <Row title="Stop on terminal" help="Stop the sandbox when the run reaches a terminal state.">
        <Toggle on={getBool(lifecycle, "stop_on_terminal") ?? false} />
      </Row>
      <Row title="Env" help="Environment variables injected into the sandbox.">
        <Count n={objectKeyCount(environment, "env")} singular="var" plural="vars" />
      </Row>
    </Panel>
  );
}

function GitPanel({ snapshot }: { snapshot: WorkflowSettings }) {
  const run = getObject(snapshot, "run");
  const author = getObject(getObject(run, "git"), "author");
  const authorName = getString(author, "name");
  const authorEmail = getString(author, "email");
  const scm = getObject(run, "scm");
  const scmProvider = getString(scm, "provider");
  const scmOwner = getString(scm, "owner");
  const scmRepo = getString(scm, "repository");
  const repoLabel = scmOwner && scmRepo ? `${scmOwner}/${scmRepo}` : scmRepo ?? scmOwner;
  const pr = getObject(run, "pull_request");
  return (
    <Panel title="Git">
      <Row title="Author" help="Identity used for commits made during the run.">
        <AuthorValue name={authorName} email={authorEmail} />
      </Row>
      <Row title="SCM" help="Source-control provider and repository.">
        {scmProvider ? (
          <span className="inline-flex flex-wrap items-center gap-x-2 gap-y-1">
            <Badge>{scmProvider}</Badge>
            {repoLabel ? <Mono>{repoLabel}</Mono> : null}
          </span>
        ) : (
          <Muted>None</Muted>
        )}
      </Row>
      <Row title="Pull request" help="Whether this run opens a PR on completion.">
        <PullRequestValue pr={pr} />
      </Row>
    </Panel>
  );
}

function ArtifactsPanel({ snapshot }: { snapshot: WorkflowSettings }) {
  const artifacts = getObject(getObject(snapshot, "run"), "artifacts");
  const include = getArray(artifacts, "include");
  return (
    <Panel title="Artifacts">
      <Row title="Include" help="Workspace-relative globs collected after each stage.">
        {include && include.length > 0 ? (
          <GlobList globs={include.filter((e): e is string => typeof e === "string")} />
        ) : (
          <Muted>None</Muted>
        )}
      </Row>
    </Panel>
  );
}

function GoalValue({ goal }: { goal: UnknownRecord | undefined }) {
  if (!goal) return <Muted>None</Muted>;
  const type = getString(goal, "type");
  const value = getString(goal, "value");
  if (!value) return <Muted>None</Muted>;
  return (
    <span className="inline-flex flex-wrap items-center gap-x-2 gap-y-1">
      {type ? <Badge>{type}</Badge> : null}
      <Mono>{value}</Mono>
    </span>
  );
}

function AuthorValue({ name, email }: { name?: string; email?: string }) {
  if (!name && !email) return <Muted>Default</Muted>;
  if (name && email) {
    return (
      <span className="text-fg-2">
        {name} <span className="text-fg-muted">&lt;{email}&gt;</span>
      </span>
    );
  }
  return <span className="text-fg-2">{name ?? email}</span>;
}

function PullRequestValue({ pr }: { pr: UnknownRecord | undefined }) {
  const enabled = getBool(pr, "enabled") ?? false;
  if (!enabled) return <Toggle on={false} />;
  const draft = getBool(pr, "draft") ?? false;
  const autoMerge = getBool(pr, "auto_merge") ?? false;
  const strategy = getString(pr, "merge_strategy");
  return (
    <span className="inline-flex flex-wrap items-center gap-x-2 gap-y-1">
      <Toggle on={true} />
      {draft ? <Badge>draft</Badge> : null}
      {autoMerge ? <Badge>auto-merge</Badge> : null}
      {strategy ? (
        <span className="font-mono text-xs text-fg-3">{strategy}</span>
      ) : null}
    </span>
  );
}

function GlobList({ globs }: { globs: string[] }) {
  return (
    <span className="inline-flex flex-wrap items-center gap-1.5">
      {globs.map((g) => (
        <Badge key={g}>{g}</Badge>
      ))}
    </span>
  );
}
