import { Alert, Box, Group, Loader, Stack, Text } from "@mantine/core";
import { useQuery } from "@tanstack/react-query";
import { AlertCircle, CheckCircle2, FileText, FileUp } from "lucide-react";
import { SmartFileInput } from "@edr/ui-common";
import { useEffect, useState, type MutableRefObject } from "react";
import { type UseFormReturn } from "react-hook-form";

import useAuth from "@/hooks/useAuth";
import { api } from "@/services/api";
import {
  type ContractDocuments,
  type ContractFormInputValues,
  type ContractFormValues,
} from "./schema";
import { operationToProfileType } from "./helpers";
import { StepCard, StepHeader } from "./shared";

type ContractForm = UseFormReturn<
  ContractFormInputValues,
  any,
  ContractFormValues
>;

function documentSettingCode(nationality: string | null | undefined): string {
  return nationality === "foreign"
    ? "company_onboarding_documents_foreign"
    : "company_onboarding_documents_ethiopian";
}

function formatSize(bytes?: number): string {
  if (!bytes) return "";
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

/** Loosely normalise a label/key so "Investment License" ≈ "investment_license". */
function norm(s: string): string {
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "");
}

function hasUploaded(value: File | File[] | null | undefined): boolean {
  if (!value) return false;
  return Array.isArray(value) ? value.length > 0 : true;
}

/**
 * Contract intake documents step (doc §7 step 5). Documents default to what the
 * company uploaded during onboarding; whatever onboarding does NOT already cover
 * the customer must upload here. Every *required* document must be satisfied —
 * either on file from the profile or freshly uploaded — before the wizard can
 * advance. The page registers a validator the wizard calls on "Continue".
 */
export function StepDocuments({
  form,
  validatorRef,
}: {
  form: ContractForm;
  validatorRef?: MutableRefObject<(() => boolean) | null>;
}) {
  const auth = useAuth();
  const [errors, setErrors] = useState<Record<string, string>>({});

  const nationality = auth.company?.company?.nationality as
    | string
    | null
    | undefined;

  const docSettingQuery = useQuery(
    api.fileUploadSettings.getByCode.queryOptions({
      input: { code: documentSettingCode(nationality) },
    }),
  );

  const onboardingDocs = (() => {
    const profiles = auth.company?.company?.companyProfiles ?? [];
    const operationType = form.watch("operationType");
    const targetType = operationType
      ? operationToProfileType(
          operationType,
          profiles.map((p) => p.type),
        )
      : null;
    const active =
      (targetType ? profiles.find((p) => p.type === targetType) : undefined) ??
      profiles[0];
    return active?.licenseFiles ?? [];
  })();

  // Normalised names of the documents already on file from onboarding, so a
  // required field can be satisfied by a profile document instead of an upload.
  const onFileKeys = new Set(
    onboardingDocs
      .map((d) => norm((d as { name?: string }).name ?? ""))
      .filter(Boolean),
  );

  const documents = (form.watch("documents") ?? {}) as ContractDocuments;

  const setDocuments = (next: Record<string, File | File[] | null>) => {
    form.setValue("documents", next, { shouldDirty: true });
    // Clear errors for fields that now have an upload.
    setErrors((prev) => {
      const updated = { ...prev };
      for (const key of Object.keys(updated)) {
        if (hasUploaded(next[key])) delete updated[key];
      }
      return updated;
    });
  };

  const fields = docSettingQuery.data?.fields ?? [];

  // A required field is satisfied if it's uploaded here OR already on file from
  // onboarding (matched by fileKey or label). Returns the list of missing keys.
  const missingRequired = (): string[] =>
    fields
      .filter((f) => f.isRequired)
      .filter((f) => {
        if (hasUploaded(documents[f.fileKey])) return false;
        const onFile =
          onFileKeys.has(norm(f.fileKey)) ||
          onFileKeys.has(norm(f.fileLabel));
        return !onFile;
      })
      .map((f) => f.fileKey);

  // Register the validator the wizard calls before advancing past this step.
  useEffect(() => {
    if (!validatorRef) return;
    validatorRef.current = () => {
      const missing = missingRequired();
      if (missing.length === 0) {
        setErrors({});
        return true;
      }
      const fieldsMap = new Map(fields.map((f) => [f.fileKey, f]));
      const next: Record<string, string> = {};
      for (const key of missing) {
        next[key] = `${fieldsMap.get(key)?.fileLabel ?? "This document"} is required.`;
      }
      setErrors(next);
      return false;
    };
    return () => {
      if (validatorRef) validatorRef.current = null;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [validatorRef, fields, documents, onboardingDocs]);

  const hasErrors = Object.keys(errors).length > 0;

  return (
    <StepCard>
      <StepHeader
        icon={<FileUp size={22} />}
        title="Contract Documents"
        description="Attach the documents required for this contract. Anything already on file from your onboarding is reused automatically — upload the rest here. Required documents must be provided to continue."
      />

      {onboardingDocs.length > 0 && (
        <Stack gap={10} mb="lg">
          <Text fz={13} fw={700} c="#10202F">
            On file from your onboarding
          </Text>
          {onboardingDocs.map((doc, i) => (
            <Group
              key={`${doc.id}-${i}`}
              gap={12}
              align="center"
              wrap="nowrap"
              className="rounded-xl"
              style={{
                border: "1px solid var(--mantine-color-edr-border-0)",
                padding: "12px 16px",
              }}
            >
              <Box
                style={{
                  flexShrink: 0,
                  width: 36,
                  height: 36,
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                  borderRadius: 10,
                  backgroundColor: "#EAF1FB",
                  color: "#2E5B96",
                }}
              >
                <FileText size={18} />
              </Box>
              <Box style={{ minWidth: 0, flex: 1 }}>
                <Text size="sm" fw={600} truncate>
                  {doc.name}
                </Text>
                {doc.size ? (
                  <Text size="xs" c="dimmed">
                    {formatSize(doc.size)}
                  </Text>
                ) : null}
              </Box>
              <Box
                style={{
                  flexShrink: 0,
                  display: "flex",
                  alignItems: "center",
                  gap: 6,
                  color: "#0A6F4D",
                }}
              >
                <CheckCircle2 size={15} />
                <Text size="xs" fw={600} c="#0A6F4D">
                  On file
                </Text>
              </Box>
            </Group>
          ))}
        </Stack>
      )}

      <Text fz={13} fw={700} c="#10202F" mb="sm">
        Documents for this contract
      </Text>

      {hasErrors && (
        <Alert
          color="red"
          icon={<AlertCircle size={16} />}
          radius="md"
          mb="md"
        >
          <Text size="sm" fw={600}>
            Please provide all required documents
          </Text>
          <Text size="sm" mt={2} c="red.7">
            Upload the documents marked in red below, or make sure they are on
            file from your onboarding.
          </Text>
        </Alert>
      )}

      {docSettingQuery.isLoading ? (
        <Group justify="center" py="lg">
          <Loader size="sm" color="edr-green" />
        </Group>
      ) : docSettingQuery.data ? (
        <SmartFileInput
          file={docSettingQuery.data}
          value={documents}
          onChange={setDocuments}
          errors={errors}
        />
      ) : (
        <Text size="sm" c="dimmed">
          No document requirements are configured for your account. The
          documents on file from your onboarding will be attached to this
          contract automatically.
        </Text>
      )}
    </StepCard>
  );
}
