import {
  Box,
  Button,
  Group,
  Modal,
  ScrollArea,
  Stack,
  Text,
  Title,
} from "@mantine/core";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
  ArrowLeft,
  ArrowRight,
  Building2,
  CheckCircle2,
  FileText,
  Globe2,
  UploadCloud,
  User,
  UserCheck,
} from "lucide-react";
import type { ReactNode } from "react";
import { useCallback, useEffect, useRef, useState } from "react";

import type { RoleLicenseProfile } from "@/components/onboarding/RoleLicenseStep";
import useAuth from "@/hooks/useAuth";
import CompanyProfileForm from "@/pages/accounts/CompanyProfileForm";
import NationalitySelect from "@/pages/settings/NationalitySelect";
import OnboardingRoleSelect from "@/pages/settings/OnboardingRoleSelect";
import { api } from "@/services/api";
import type {
  CompanyNationality,
  CreateCompanyPayload,
  ProfileTypeValue,
} from "@/services/companies.service";
import { companiesService } from "@/services/companies.service";
import type { UpdateProfilePayload } from "@/types/profile";
import { extractApiError } from "@/utils/result";

/** Form steps rendered by CompanyProfileForm. */
type FormStep =
  | "company"
  | "personnel"
  | "contact"
  | "poa"
  | "documents"
  | "additional";
const FORM_STEPS: FormStep[] = [
  "company",
  "personnel",
  "contact",
  "poa",
  "documents",
  "additional",
];

/** The full onboarding journey: the two pre-form phases + the form steps. */
type WizardStep = "nationality" | "role" | FormStep;
const WIZARD_STEPS: WizardStep[] = ["nationality", "role", ...FORM_STEPS];

/** Icon + title + description shown in the global dialog header per step. */
const STEP_META: Record<
  WizardStep,
  { icon: ReactNode; title: string; description: string }
> = {
  nationality: {
    icon: <Globe2 size={20} />,
    title: "Where is your company registered?",
    description: "This determines the documents we'll ask you to provide.",
  },
  role: {
    icon: <Building2 size={20} />,
    title: "What does your company do?",
    description:
      "Pick any combination of Importer, Exporter and Freight Forwarder — each is set up with its own business license.",
  },
  company: {
    icon: <Building2 size={20} />,
    title: "Company Information",
    description: "Tell us about your company and its registration details.",
  },
  personnel: {
    icon: <User size={20} />,
    title: "General Manager",
    description: "Who is the general manager of the company?",
  },
  contact: {
    icon: <UserCheck size={20} />,
    title: "Contact Person",
    description: "Who should we reach out to about this account?",
  },
  poa: {
    icon: <FileText size={20} />,
    title: "Power of Attorney",
    description: "Optionally add a representative with power of attorney.",
  },
  documents: {
    icon: <UploadCloud size={20} />,
    title: "Upload Documents",
    description: "Provide the required company documents.",
  },
  additional: {
    icon: <CheckCircle2 size={20} />,
    title: "Business License",
    description: "Upload a business license for each operational profile.",
  },
};

interface OnboardingWizardDialogProps {
  opened: boolean;
  /** Dismiss the dialog (user clicked the close icon). */
  onClose: () => void;
}

/**
 * The company type for the onboarding selection. Importer / Exporter / Freight
 * Forwarder are all services a single "customer" company can hold (in any
 * combination), each with its own business license — so the company is always
 * registered as a "customer".
 */
function companyTypeForRoles(_roles: string[]): string {
  return "customer";
}

/** Document upload setting code per company nationality. */
function documentSettingCode(nationality: CompanyNationality): string {
  return nationality === "foreign"
    ? "company_onboarding_documents_foreign"
    : "company_onboarding_documents_ethiopian";
}

/**
 * First-run onboarding wizard with a "draft-first" flow: picking the role(s)
 * immediately creates a draft company + profile on the backend, so every
 * subsequent step saves its data incrementally (PATCH /profile, /onboarding-step)
 * against existing rows. The final step uploads documents and marks onboarding
 * complete. Dismissable — the gate keeps it reachable until finished.
 */
export default function OnboardingWizardDialog({
  opened,
  onClose,
}: OnboardingWizardDialogProps) {
  const queryClient = useQueryClient();
  const { user, company, onboardingStep } = useAuth();

  const existingProfiles = company?.company?.companyProfiles ?? [];
  const companyAlreadyStarted = Boolean(company?.company?.id);
  const savedNationality =
    (company?.company?.nationality as CompanyNationality | null) ?? null;

  // Resume position from the backend-persisted step.
  const resumeFormStep: FormStep = FORM_STEPS.includes(onboardingStep as FormStep)
    ? (onboardingStep as FormStep)
    : "company";

  // Phases: nationality → role → form. If a draft already exists, resume
  // straight into the form with nationality + roles pre-selected.
  const [phase, setPhase] = useState<"nationality" | "role" | "form">(
    companyAlreadyStarted ? "form" : "nationality",
  );
  const [nationality, setNationality] = useState<CompanyNationality | null>(
    savedNationality,
  );
  const [roles, setRoles] = useState<string[]>(
    existingProfiles.map((p) => p.type),
  );
  const [documentFiles, setDocumentFiles] = useState<
    Record<string, File | File[] | null>
  >({});
  // Newly-selected business-license files per company_profile id.
  const [licenseFiles, setLicenseFiles] = useState<Record<string, File[]>>({});
  const [startError, setStartError] = useState<string | null>(null);
  // Mirror of CompanyProfileForm's active step so the global header + progress
  // pill can reflect it (the form no longer renders its own stepper).
  const [formStep, setFormStep] = useState<FormStep>(resumeFormStep);

  // Saved profile data, for rehydrating the form fields after a refresh.
  const profileQuery = useQuery(
    api.companies.getProfile.queryOptions({
      enabled: companyAlreadyStarted,
      retry: false,
      refetchOnWindowFocus: false,
    }),
  );

  const refreshInfo = useCallback(
    () =>
      queryClient.invalidateQueries({
        queryKey: api.companies.getInfo.queryKey(),
      }),
    [queryClient],
  );

  // Begin onboarding: create the draft company + profile + role(s) + nationality.
  const startMutation = useMutation({
    mutationFn: (vars: {
      companyType: string;
      roles: ProfileTypeValue[];
      nationality?: CompanyNationality;
    }) => api.companies.startOnboarding.call(vars),
    onSuccess: async () => {
      await refreshInfo();
      setPhase("form");
    },
    onError: (err) => setStartError(extractApiError(err).message),
  });

  // Finalize: upload per-role license files + company documents, then complete.
  const finishMutation = useMutation({
    mutationFn: async () => {
      const companyId = company?.company?.id;
      // Per-role business licenses (file model, resource=company_profiles).
      for (const [profileId, files] of Object.entries(licenseFiles)) {
        if (files.length > 0) {
          await companiesService.uploadProfileLicense(profileId, files);
        }
      }
      // Nationality-based company documents (resource=companies).
      const hasDocs = Object.values(documentFiles).some(
        (f) => f !== null && (Array.isArray(f) ? f.length > 0 : true),
      );
      if (companyId && hasDocs) {
        await companiesService.uploadDocuments(companyId, documentFiles);
      }
      return api.companies.completeOnboarding.call();
    },
    onSuccess: refreshInfo,
    onError: (err) => setStartError(extractApiError(err).message),
  });

  // Persist the resume step to the backend, but only ever move FORWARD — going
  // Back must never downgrade the furthest step the user reached, so reopening
  // always lands on the furthest step.
  const furthestIdxRef = useRef(FORM_STEPS.indexOf(resumeFormStep));
  const persistStep = useCallback((step: string) => {
    const idx = FORM_STEPS.indexOf(step as FormStep);
    if (idx < 0 || idx <= furthestIdxRef.current) return;
    furthestIdxRef.current = idx;
    api.companies.setOnboardingStep.call({ step }).catch(() => {});
  }, []);

  // Mirror the form's step locally (for the header/pill) and persist it.
  const handleStepChange = useCallback(
    (step: string) => {
      setFormStep(step as FormStep);
      persistStep(step);
    },
    [persistStep],
  );

  // The company query may resolve AFTER this dialog mounts (it's kept mounted by
  // the gate), so the phase/roles/nationality initial state can be stale — a
  // draft that already exists would otherwise leave us stuck on the first
  // (nationality) phase. Once a draft loads, jump straight into the form with
  // the persisted roles/nationality. Runs once per resumed draft.
  const resumedRef = useRef(false);
  useEffect(() => {
    if (!companyAlreadyStarted || resumedRef.current) return;
    resumedRef.current = true;
    setRoles(existingProfiles.map((p) => p.type));
    setNationality(savedNationality);
    setPhase("form");
    const idx = FORM_STEPS.indexOf(resumeFormStep);
    if (idx > furthestIdxRef.current) furthestIdxRef.current = idx;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [companyAlreadyStarted, resumeFormStep]);

  const handleNationalityContinue = useCallback(() => {
    if (nationality) setPhase("role");
  }, [nationality]);

  const handleRolesContinue = useCallback(() => {
    setStartError(null);
    startMutation.mutate({
      companyType: companyTypeForRoles(roles),
      roles: roles as ProfileTypeValue[],
      nationality: nationality ?? undefined,
    });
  }, [roles, nationality, startMutation]);

  // Note: no "back to role selection" — once the draft is created the role(s)
  // are fixed; the form's first-step Back is a no-op so progress never resets.
  const handleBackToRoles = useCallback(() => {}, []);

  // Save the current step's fields to the draft (PATCH /profile). Returns the
  // server error message on failure so the form can show it (e.g. duplicate TIN).
  const saveStep = useCallback(
    async (
      data: Partial<UpdateProfilePayload>,
    ): Promise<{ ok: true } | { ok: false; error: string }> => {
      try {
        await api.companies.updateProfile.call(data as UpdateProfilePayload);
        return { ok: true };
      } catch (err) {
        return { ok: false, error: extractApiError(err).message };
      }
    },
    [],
  );

  // Final confirm step → finalize onboarding (no company create; it already
  // exists as a draft that's been filled in step-by-step).
  const handleSubmit = useCallback(
    (_payload: CreateCompanyPayload) => {
      finishMutation.mutate();
    },
    [finishMutation],
  );

  if (!user) return null;

  // Any non-empty combination of importer/exporter/freight-forwarder is valid.
  const rolesValid = roles.length > 0;
  // Documents depend on nationality; fall back to the saved one (resume) then ethiopian.
  const effectiveNationality: CompanyNationality =
    nationality ?? savedNationality ?? "ethiopian";

  // Per-role license cards for the final step (from the created profiles).
  const roleProfiles: RoleLicenseProfile[] = existingProfiles.map((p) => ({
    id: p.id,
    type: p.type,
    reference: p.reference,
    existingFiles: p.licenseFiles ?? [],
  }));

  // The active step across the whole journey, driving the header + progress pill.
  const activeStep: WizardStep = phase === "form" ? formStep : phase;
  const stepMeta = STEP_META[activeStep];
  const activeIdx = WIZARD_STEPS.indexOf(activeStep);

  const formProps = {
    documentSettingCode: documentSettingCode(effectiveNationality),
    documentFiles,
    onDocumentFilesChange: setDocumentFiles,
    user,
    onSubmit: handleSubmit,
    isPending: finishMutation.isPending,
    onBack: handleBackToRoles,
    hideFirstStepBack: true,
    initialStep: resumeFormStep,
    resyncOpen: opened,
    onStepChange: handleStepChange,
    onSaveStep: saveStep,
    rehydrate: profileQuery.data ?? null,
    roleProfiles,
    licenseFiles,
    onLicenseChange: setLicenseFiles,
  };

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      withCloseButton
      closeOnClickOutside={false}
      closeOnEscape
      size={1040}
      radius="lg"
      padding="xl"
      centered
      keepMounted
      scrollAreaComponent={ScrollArea.Autosize}
      overlayProps={{ backgroundOpacity: 0.55, blur: 4 }}
      styles={{
        header: {
          alignItems:"flex-start"
        },
        title: {
          flex: 1
        }
      }}
      title={
  <Stack gap="md">
          <Box>
            <Group gap="sm" mb={4}>
              {stepMeta.icon}
              <Title order={3}>{stepMeta.title}</Title>
            </Group>
            <Text c="edr-muted" size="sm">
              {stepMeta.description}
            </Text>
          </Box>
          <ProgressPill current={activeIdx} total={WIZARD_STEPS.length} />
        </Stack>
      }
    >
      <Stack gap="xl">

        {phase === "nationality" ? (
          <Stack gap="lg">
            <NationalitySelect
              value={nationality}
              onChange={setNationality}
              embedded
            />
            <Group justify="flex-end" pt="xs">
              <Button
                color="edr-green"
                onClick={handleNationalityContinue}
                disabled={!nationality}
                rightSection={<ArrowRight size={16} />}
              >
                Continue
              </Button>
            </Group>
          </Stack>
        ) : phase === "role" ? (
          <Stack gap="lg">
            <OnboardingRoleSelect value={roles} onChange={setRoles} embedded />
            {startError && (
              <Text size="sm" c="red">
                {startError}
              </Text>
            )}
            <Group justify="space-between" pt="xs">
              <Button
                variant="default"
                leftSection={<ArrowLeft size={16} />}
                onClick={() => setPhase("nationality")}
              >
                Back
              </Button>
              <Button
                color="edr-green"
                onClick={handleRolesContinue}
                disabled={!rolesValid}
                loading={startMutation.isPending}
                rightSection={
                  startMutation.isPending ? undefined : <ArrowRight size={16} />
                }
              >
                Continue
              </Button>
            </Group>
          </Stack>
        ) : (
          <CompanyProfileForm {...formProps} />
        )}
      </Stack>
    </Modal>
  );
}

/**
 * Continuous progress pill: a single rounded track that fills left-to-right as
 * the user advances, with faint ticks marking each step boundary.
 */
function ProgressPill({ current, total }: { current: number; total: number }) {
  const pct = total > 0 ? ((current + 1) / total) * 100 : 0;
  return (
    <Box className="relative h-1.5 w-full overflow-hidden rounded-full bg-edr-border">
      <Box
        className="absolute inset-y-0 left-0 rounded-full bg-[var(--mantine-color-edr-green-6)] transition-[width] duration-500 ease-out"
        style={{ width: `${pct}%` }}
      />
    </Box>
  );
}
