import { useState, useCallback } from "react";
import type { OrderType, AllocationType } from "@capakraken/shared";
import { ProjectStatus, AllocationStatus } from "@capakraken/shared";
import { trpc } from "~/lib/trpc/client.js";
import { makeDefaultState, type WizardState } from "./types.js";

// ─── Standalone pure validation function ─────────────────────────────────────

export function canGoNext(step: number, state: WizardState): boolean {
  if (step === 0) {
    const missingRequired = state.blueprintFieldDefs.some((f) => {
      if (!f.required) return false;
      const val = state.dynamicFields[f.key];
      return (
        val === undefined || val === null || val === "" || (Array.isArray(val) && val.length === 0)
      );
    });
    return (
      state.shortCode.trim().length > 0 &&
      /^[A-Z0-9_-]+$/.test(state.shortCode) &&
      state.name.trim().length > 0 &&
      !missingRequired
    );
  }
  if (step === 1) {
    return (
      !!state.startDate &&
      !!state.endDate &&
      state.endDate >= state.startDate &&
      (state.budgetEur === "" || parseFloat(state.budgetEur) >= 0)
    );
  }
  if (step === 2) {
    return state.staffingReqs.every(
      (r) =>
        (r.roleId != null || r.role.trim().length > 0) && r.hoursPerDay > 0 && r.headcount >= 1,
    );
  }
  return true;
}

// ─── Hook options ─────────────────────────────────────────────────────────────

export interface UseProjectWizardFormOptions {
  onClose: () => void;
  onSuccess?: ((shortCode: string, name: string) => void) | undefined;
}

// ─── Hook ─────────────────────────────────────────────────────────────────────

export function useProjectWizardForm({ onClose, onSuccess }: UseProjectWizardFormOptions) {
  const utils = trpc.useUtils();
  const [step, setStep] = useState(0);
  const [state, setState] = useState<WizardState>(makeDefaultState);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState<string | null>(null);
  const [submitWarnings, setSubmitWarnings] = useState<string[]>([]);
  const [showConfetti, setShowConfetti] = useState(false);
  const [showSuccessToast, setShowSuccessToast] = useState(false);

  const createProject = trpc.project.create.useMutation();
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const createAssignment = (trpc.allocation.createAssignment.useMutation as any)() as {
    mutateAsync: (input: unknown) => Promise<unknown>;
  };

  const createDemandRequirement = (
    trpc.allocation.createDemandRequirement.useMutation as any
  )() as {
    mutateAsync: (input: unknown) => Promise<unknown>;
  };

  const patch = useCallback((p: Partial<WizardState>) => {
    setState((prev) => ({ ...prev, ...p }));
  }, []);

  function reset() {
    setStep(0);
    setState(makeDefaultState());
    setIsSubmitting(false);
    setSubmitError(null);
    setSubmitWarnings([]);
  }

  function handleClose() {
    reset();
    onClose();
  }

  async function handleSubmit() {
    setIsSubmitting(true);
    setSubmitError(null);
    setSubmitWarnings([]);

    try {
      const project = await createProject.mutateAsync({
        shortCode: state.shortCode.trim(),
        name: state.name.trim(),
        orderType: state.orderType as unknown as OrderType,
        allocationType: state.allocationType as unknown as AllocationType,
        winProbability: state.winProbability,
        budgetCents: state.budgetEur ? Math.round(parseFloat(state.budgetEur) * 100) : 0,
        startDate: new Date(state.startDate),
        endDate: new Date(state.endDate),
        staffingReqs: state.staffingReqs,
        status: state.saveAsDraft ? ProjectStatus.DRAFT : ProjectStatus.ACTIVE,
        responsiblePerson: state.responsiblePerson.trim(),
        blueprintId: state.blueprintId ?? undefined,
        dynamicFields: state.dynamicFields,
      });

      const warnings: string[] = [];

      // Create draft assignments for assigned resources
      for (const assignment of state.assignments) {
        try {
          const req = state.staffingReqs.find((r) => r.id === assignment.requirementId);
          const hoursPerDay = req?.hoursPerDay ?? 8;
          const percentage = Math.min(100, Math.round((hoursPerDay / 8) * 100));
          await createAssignment.mutateAsync({
            projectId: project.id,
            resourceId: assignment.resourceId,
            startDate: new Date(state.startDate),
            endDate: new Date(state.endDate),
            hoursPerDay,
            percentage,
            role: assignment.role,
            roleId: req?.roleId,
            status: AllocationStatus.PROPOSED,
            metadata: {},
          });
        } catch (err) {
          const msg = err instanceof Error ? err.message : String(err);
          warnings.push(`Assignment for "${assignment.resourceName}" failed: ${msg}`);
        }
      }

      // Create open demand for unassigned slots
      for (const req of state.staffingReqs) {
        const assignedCount = state.assignments.filter((a) => a.requirementId === req.id).length;
        const unassigned = req.headcount - assignedCount;
        if (unassigned <= 0) continue;
        try {
          await createDemandRequirement.mutateAsync({
            projectId: project.id,
            startDate: new Date(state.startDate),
            endDate: new Date(state.endDate),
            hoursPerDay: req.hoursPerDay,
            percentage: Math.min(100, Math.round((req.hoursPerDay / 8) * 100)),
            role: req.role || undefined,
            roleId: req.roleId,
            headcount: unassigned,
            status: AllocationStatus.PROPOSED,
            metadata: {},
          });
        } catch (err) {
          const msg = err instanceof Error ? err.message : String(err);
          warnings.push(
            `Demand for "${req.role || "Unnamed Role"}" (${unassigned} seat${unassigned > 1 ? "s" : ""}) failed: ${msg}`,
          );
        }
      }

      if (warnings.length > 0) setSubmitWarnings(warnings);

      await utils.project.listWithCosts.invalidate();
      await utils.timeline.getEntries.invalidate();
      await utils.timeline.getEntriesView.invalidate();
      setShowConfetti(true);
      setShowSuccessToast(true);
      setTimeout(() => {
        setShowConfetti(false);
        onSuccess?.(project.shortCode, project.name);
        handleClose();
      }, 1200);
    } catch (err) {
      let errorMessage = "Failed to create project";
      if (err instanceof Error) {
        try {
          const parsed: unknown = JSON.parse(err.message);
          if (Array.isArray(parsed) && parsed.length > 0) {
            errorMessage = (parsed as { message?: string }[])
              .map((e) => e.message)
              .filter(Boolean)
              .join("; ");
          } else {
            errorMessage = err.message;
          }
        } catch {
          errorMessage = err.message;
        }
      }
      setSubmitError(errorMessage);
    } finally {
      setIsSubmitting(false);
    }
  }

  return {
    step,
    setStep,
    state,
    patch,
    reset,
    canGoNext: () => canGoNext(step, state),
    isSubmitting,
    submitError,
    submitWarnings,
    showConfetti,
    showSuccessToast,
    setShowSuccessToast,
    handleSubmit,
    handleClose,
  };
}
