import { commandIds } from "@commands/ids";
import type { GenerationCandidate, GenerationCompareMode, GenerationState } from "@editor/state";
import type { GenerateSettings } from "@editor/tools";
import type { AppStore } from "@editor/store";
import type { GenerationWorkflow } from "@operations/generation/workflow";
import { currentGenerationJob, GenerationJobStatus } from "../GenerationJobStatus";

export type GenerateActionControlsProps = {
  settings: GenerateSettings;
  generation: GenerationState;
  dispatch: AppStore["dispatch"];
  workflow: GenerationWorkflow;
  generate: () => Promise<void>;
};

export function GenerateActionControls({ settings, generation, dispatch, workflow, generate }: GenerateActionControlsProps) {
  const job = currentGenerationJob(generation);
  const busy = job?.status === "running";
  const candidate = selectedCandidate(generation);
  const precondition = workflow.precondition();
  const canGenerate = precondition.ready && !busy;
  const preconditionMessage = precondition.ready ? undefined : precondition.message;
  const repair = precondition.ready ? undefined : precondition.repair;

  return (
    <div className="flex max-w-[calc(100vw-2rem)] flex-nowrap items-center justify-start gap-2 whitespace-nowrap px-2">
      <button
        type="button"
        disabled={!canGenerate}
        className="h-9 rounded-lg bg-sky-300 px-5 text-xs font-semibold !text-slate-950 transition hover:bg-sky-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/50 disabled:pointer-events-none disabled:opacity-35"
        title={job?.status === "failed" ? job.error : preconditionMessage ?? "Generate with ComfyUI"}
        onClick={() => {
          void generate();
        }}
      >
        {busy && job?.kind === "generate" ? "Generating..." : "Generate"}
      </button>
      {preconditionMessage ? (
        <span className="flex max-w-80 items-center gap-2 rounded-md border border-amber-300/10 bg-amber-300/[0.07] px-2.5 py-1.5 text-xs text-amber-100/70" role="status">
          <span>{preconditionMessage}</span>
          {repair === "add-mask" ? (
            <button type="button" className="shrink-0 rounded-md bg-white px-2.5 py-1 font-semibold text-black" onClick={() => void workflow.prepareInpaintMask()}>Add mask</button>
          ) : repair === "set-outpaint-padding" ? (
            <button type="button" className="shrink-0 rounded-md bg-white px-2.5 py-1 font-semibold text-black" onClick={() => dispatch(commandIds.toolSetGenerateSettings, { outpaint: { ...settings.outpaint, left: 128, right: 128 } })}>Add padding</button>
          ) : null}
        </span>
      ) : null}
      <GenerationJobStatus generation={generation} />
      {busy ? <button type="button" className="h-8 rounded-md bg-white/[0.05] px-2.5 text-xs font-semibold text-white/65 hover:bg-white/[0.09] hover:text-white" onClick={workflow.cancel}>Cancel</button> : null}
      {candidate ? (
        <>
          <CandidatePicker generation={generation} dispatch={dispatch} />
          <CandidateControls
            candidate={candidate}
            compareMode={generation.compareMode ?? "result"}
            settings={settings}
            busy={busy}
            dispatch={dispatch}
            workflow={workflow}
          />
        </>
      ) : null}
    </div>
  );
}

function CandidatePicker({ generation, dispatch }: { generation: GenerationState; dispatch: AppStore["dispatch"] }) {
  return (
    <div className="subtle-scrollbar flex max-w-[min(34rem,calc(100vw-2rem))] items-stretch gap-1.5 overflow-x-auto border-l border-white/[0.08] pl-2" role="group" aria-label="Provisional generation candidates">
      {generation.candidates.map((candidate) => {
        const selected = candidate.id === (generation.selectedCandidateId ?? generation.candidates[0]?.id);
        return (
          <button
            key={candidate.id}
            type="button"
            className={`relative h-16 w-16 shrink-0 overflow-hidden rounded-lg border transition ${selected ? "border-sky-300 shadow-[0_0_0_1px_rgba(125,211,252,0.35)]" : "border-white/10 hover:border-white/30"}`}
            title={`Provisional candidate · seed ${candidate.seed}`}
            onClick={() => dispatch(commandIds.generationSelectCandidate, { candidateId: candidate.id })}
          >
            <img src={candidate.source} alt="" className="h-full w-full object-cover" />
            {selected ? <span className="absolute inset-x-1 bottom-1 rounded bg-black/75 px-1 py-0.5 text-[0.58rem] font-semibold uppercase tracking-wide text-sky-100">Reviewing</span> : null}
          </button>
        );
      })}
      <button
        type="button"
        disabled={generation.candidates.length === 0}
        className="min-h-16 shrink-0 rounded-xl px-3 text-xs font-semibold text-white/55 transition hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-35"
        title="Dismiss every candidate and reset comparison"
        onClick={() => dispatch(commandIds.generationClearCandidates, undefined)}
      >
        Clear all
      </button>
    </div>
  );
}

function CandidateControls({
  candidate,
  compareMode,
  settings,
  busy,
  dispatch,
  workflow,
}: {
  candidate: GenerationCandidate;
  compareMode: GenerationCompareMode;
  settings: GenerateSettings;
  busy: boolean;
  dispatch: AppStore["dispatch"];
  workflow: GenerationWorkflow;
}) {
  const rerun = (label: string, nextSettings: GenerateSettings) => {
    void workflow.regenerate(candidate.id, nextSettings, label);
  };
  const disabled = busy;

  return (
    <div className="flex flex-nowrap items-center justify-center gap-1 border-l border-white/[0.08] pl-2">
      <CandidatePreview candidate={candidate} />
      <span className="px-2 text-xs font-medium text-white/55" title={`${candidate.settings.model} · ${candidate.mode}`}><strong className="block font-semibold text-white/75">Provisional result</strong>Seed {candidate.seed}</span>
      <CandidateCompareControls compareMode={compareMode} disabled={disabled} dispatch={dispatch} />
      <CandidateButton disabled={disabled} label="Regenerate" title="Regenerate same mask and crop" onClick={() => rerun("Regenerate", candidate.settings)} />
      <CandidateButton disabled={disabled || !candidate.inpaint} label="Current mask" title="Rebuild the crop and generate from the current AI edit region" onClick={() => void workflow.rebuildFromCurrentRegion(candidate.id)} />
      <CandidateButton
        disabled={disabled}
        label="Lower"
        title="Lower strength and regenerate same mask"
        onClick={() => rerun("Lower", { ...candidate.settings, strength: Math.max(0, candidate.settings.strength - 10), seed: candidate.seed })}
      />
      <CandidateButton disabled={disabled} label="Reuse seed" title="Regenerate with the same seed" onClick={() => rerun("Reuse seed", { ...candidate.settings, seed: candidate.seed })} />
      <CandidateButton disabled={disabled} label="New seed" title="Regenerate with a new seed" onClick={() => rerun("New seed", { ...candidate.settings, seed: -1 })} />
      <CandidateButton
        disabled={disabled}
        label="Use settings"
        title="Restore this candidate's prompt, model, generation settings, and resolved seed"
        onClick={() => dispatch(commandIds.generationReuseCandidateSettings, { candidateId: candidate.id })}
      />
      <CandidateButton disabled={disabled} label="Add as layer" title="Add candidate to the document as a layer" onClick={() => workflow.applyCandidateAsLayer(candidate.id)} />
      <CandidateButton
        disabled={disabled}
        label="Add + mask"
        title="Add candidate as a layer with a fresh refinement mask"
        onClick={() => {
          void workflow.applyCandidateAsRefinementLayer(candidate.id);
        }}
      />
      <CandidateButton
        disabled={disabled || !candidate.inpaint}
        label="Replace pixels"
        title={candidate.inpaint ? "Replace masked pixels and preserve unmasked pixels" : "Only inpaint candidates can replace masked pixels"}
        onClick={() => {
          void workflow.replaceCandidatePixels(candidate.id);
        }}
      />
      <CandidateButton
        disabled={disabled || !candidate.inpaint}
        label="Edit mask"
        title={candidate.inpaint ? "Paint refinement mask over the source layer" : "Only inpaint candidates have a mask to edit"}
        onClick={() => {
          if (!candidate.inpaint) return;
          dispatch(commandIds.selectionSet, { artboardId: candidate.placement.artboardId, layerIds: [candidate.inpaint.targetLayerId] });
          dispatch(commandIds.toolEnterInpaintRegionEdit, { targetLayerId: candidate.inpaint.targetLayerId, regionId: candidate.inpaint.regionId });
        }}
      />
      <CandidateButton disabled={disabled} label="Dismiss" title="Remove this candidate" onClick={() => dispatch(commandIds.generationRemoveCandidate, { candidateId: candidate.id })} />
      {settings.seed !== candidate.seed ? null : <span className="sr-only">Current settings use this candidate's resolved seed</span>}
    </div>
  );
}

function CandidateCompareControls({ compareMode, disabled, dispatch }: { compareMode: GenerationCompareMode; disabled: boolean; dispatch: AppStore["dispatch"] }) {
  return (
    <span className="flex items-center gap-0.5 border-l border-white/[0.08] pl-1" aria-label="Compare candidate">
      {generationCompareOptions.map((option) => {
        const active = compareMode === option.mode;
        return (
          <button
            key={option.mode}
            type="button"
            className={`h-7 rounded px-2 text-[0.68rem] font-semibold transition ${
              active ? "bg-white text-black" : "text-white/55 hover:bg-white/10 hover:text-white"
            } disabled:pointer-events-none disabled:opacity-35`}
            disabled={disabled}
            title={option.title}
            onClick={() => dispatch(commandIds.generationSetCompareMode, { mode: option.mode })}
          >
            {option.label}
          </button>
        );
      })}
    </span>
  );
}

const generationCompareOptions: Array<{ mode: GenerationCompareMode; label: string; title: string }> = [
  { mode: "result", label: "After", title: "Show the generated result over the document" },
  { mode: "before", label: "Before", title: "Hide the generated result and show the source document" },
  { mode: "split", label: "Split", title: "Compare source on the left with result on the right" },
];

function CandidatePreview({ candidate }: { candidate: GenerationCandidate }) {
  if (!candidate.inputImage) {
    return <img src={candidate.source} alt="" className="h-10 w-10 rounded-md bg-black/25 object-cover ring-1 ring-white/10" />;
  }

  return (
    <span className="flex items-center -space-x-2" title={candidate.maskImage ? "Input crop, normalized mask, generated candidate" : "Before and generated candidate"}>
      <img src={candidate.inputImage} alt="" className="h-10 w-10 rounded-md bg-black/25 object-cover ring-1 ring-white/10" />
      {candidate.maskImage ? <img src={candidate.maskImage} alt="" className="h-10 w-10 rounded-md bg-black/25 object-cover ring-1 ring-white/20" /> : null}
      <img src={candidate.source} alt="" className="h-10 w-10 rounded-md bg-black/25 object-cover ring-1 ring-sky-300/40" />
    </span>
  );
}

function CandidateButton({ label, title, disabled, busy, onClick }: { label: string; title: string; disabled?: boolean; busy?: boolean; onClick: () => void }) {
  return (
    <button
      type="button"
      className="h-8 rounded-md bg-white/[0.05] px-2.5 text-xs font-semibold text-white/65 transition hover:bg-white/[0.09] hover:text-white disabled:pointer-events-none disabled:opacity-35"
      disabled={disabled}
      title={title}
      onClick={onClick}
    >
      {busy ? "..." : label}
    </button>
  );
}

function selectedCandidate(generation: GenerationState): GenerationCandidate | undefined {
  return generation.candidates.find((candidate) => candidate.id === generation.selectedCandidateId) ?? generation.candidates[0];
}
