import { useMemo } from "react";
import { Box, Button, Group, Loader, Stack, Text } from "@mantine/core";
import { SmartFileInput } from "@edr/ui-common";
import { CheckCircle2, Download, FileText } from "lucide-react";

import type { Freight } from "@edr/types";
import { useQuery } from "@tanstack/react-query";
import useAuth from "@/hooks/useAuth";
import { api } from "@/services/api";
import type { CompanyDocument } from "@/services/companies.service";
import { downloadStoredFile } from "@/services/files.service";
import { labelForDocCode } from "@/pages/bookings/resubmit/resubmitDocs";
import { BORDER, GREEN, INK } from "../contract-ui";

type DocumentsValue = Record<string, File | File[] | null>;
type ContractFile = NonNullable<Freight.IContract["files"]>[number];

/** Onboarding document setting code for the company's nationality. */
function documentSettingCode(nationality: string | null | undefined): string {
  return nationality === "foreign"
    ? "company_onboarding_documents_foreign"
    : "company_onboarding_documents_ethiopian";
}

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

/** One row per distinct doc code already on the contract (latest upload). */
function dedupeLatestByCode(files: ContractFile[]): ContractFile[] {
  const order: string[] = [];
  const latest = new Map<string, ContractFile>();
  for (const f of files) {
    if (f.code === "contract" || f.code.startsWith("signature_")) continue;
    if (!latest.has(f.code)) order.push(f.code);
    latest.set(f.code, f);
  }
  return order.map((c) => latest.get(c)!);
}

/**
 * The company's onboarding documents (TIN certificate, commercial license,
 * national ID, …) from the profile. Contracts carry these automatically on
 * save; the edit page also shows them directly so they are always visible even
 * on contracts created before the carry-over existed.
 */
export function useCompanyDocuments(): CompanyDocument[] {
  const auth = useAuth();
  const companyId = auth.company?.company?.id as string | undefined;
  const query = useQuery({
    ...api.companies.documents.queryOptions({
      input: { companyId: companyId ?? "" },
    }),
    enabled: Boolean(companyId),
  });
  return query.data ?? [];
}

/** Latest company document per code, excluding codes already on the contract. */
function profileFallbackDocs(
  companyDocs: CompanyDocument[],
  contractFiles: ContractFile[],
): CompanyDocument[] {
  const onContract = new Set(contractFiles.map((f) => f.code));
  const latest = new Map<string, CompanyDocument>();
  for (const d of companyDocs) {
    const prev = latest.get(d.code);
    if (!prev || d.uploadedAt > prev.uploadedAt) latest.set(d.code, d);
  }
  return [...latest.values()].filter((d) => !onContract.has(d.code));
}

/**
 * Document replace/upload block for an existing contract. Lists the documents
 * already on file (latest upload per code) and renders the onboarding-driven
 * `SmartFileInput` so the customer can replace any of them or attach any
 * required document that isn't on file yet. Used on the contract wizard's review
 * step when editing a CHANGES_REQUESTED contract.
 *
 * The keys returned through `onChange` are doc-setting field keys; the parent
 * wizard merges them into the form's `documents` map, which is uploaded with the
 * contract update.
 */
export function ContractDocsEditor({
  contract,
  value,
  onChange,
  errors,
}: {
  contract: Freight.IContract;
  value: DocumentsValue;
  onChange: (next: DocumentsValue) => void;
  errors?: Record<string, string>;
}) {
  const auth = useAuth();

  const nationality = auth.company?.company?.nationality as
    | string
    | null
    | undefined;
  const settingQuery = useQuery(
    api.fileUploadSettings.getByCode.queryOptions({
      input: { code: documentSettingCode(nationality) },
    }),
  );

  const companyDocs = useCompanyDocuments();
  const files = contract.files ?? [];
  const onFile = useMemo(() => {
    const contractRows = dedupeLatestByCode(files).map((f) => ({
      id: f.id,
      code: f.code,
      name: f.name,
      fromProfile: false,
    }));
    // Profile documents not yet carried onto the contract still show — they are
    // attached automatically on the next save.
    const profileRows = profileFallbackDocs(companyDocs, files).map((d) => ({
      id: d.id,
      code: d.code,
      name: d.name,
      fromProfile: true,
    }));
    return [...contractRows, ...profileRows];
  }, [files, companyDocs]);

  return (
    <Stack gap="md">
      {onFile.length > 0 && (
        <Stack gap={8}>
          <Text fz={13} fw={700} style={{ color: INK }}>
            Already on file
          </Text>
          {onFile.map((file) => (
            <Group
              key={file.id}
              gap={12}
              align="center"
              wrap="nowrap"
              className="rounded-xl"
              style={{ border: `1px solid ${BORDER}`, padding: "10px 14px" }}
            >
              <Box
                style={{
                  flexShrink: 0,
                  width: 34,
                  height: 34,
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                  borderRadius: 9,
                  backgroundColor: "#EAF1FB",
                  color: "#2E5B96",
                }}
              >
                <FileText size={17} />
              </Box>
              <Box style={{ minWidth: 0, flex: 1 }}>
                <Text fz="13px" fw={600} style={{ color: INK }} truncate>
                  {labelForDocCode(file.code)}
                </Text>
                <Text fz="12px" c="dimmed" truncate>
                  {file.name}
                </Text>
              </Box>
              <Group gap={8} wrap="nowrap">
                {file.fromProfile ? (
                  <Group gap={5} style={{ color: "#2E5B96" }}>
                    <CheckCircle2 size={14} />
                    <Text fz="11.5px" fw={600} style={{ color: "#2E5B96" }}>
                      From profile
                    </Text>
                  </Group>
                ) : (
                  <Group gap={5} c={GREEN}>
                    <CheckCircle2 size={14} />
                    <Text fz="11.5px" fw={600} c={GREEN}>
                      On file
                    </Text>
                  </Group>
                )}
                <Button
                  component="button"
                  type="button"
                  onClick={() => void downloadStoredFile(file.id, file.name)}
                  variant="default"
                  size="compact-xs"
                  radius="md"
                  leftSection={<Download size={13} />}
                >
                  Download
                </Button>
              </Group>
            </Group>
          ))}
        </Stack>
      )}

      <Box>
        <Text fz={13} fw={700} style={{ color: INK }} mb="xs">
          Update documents
        </Text>
        <Text fz="12px" c="dimmed" mb="sm">
          Replace any document you need to change. Documents marked required must
          be on file before you can resubmit.
        </Text>

        {settingQuery.isLoading ? (
          <Group justify="center" py="lg">
            <Loader size="sm" color="edr-green" />
          </Group>
        ) : settingQuery.data ? (
          <SmartFileInput
            file={settingQuery.data}
            value={value}
            onChange={onChange}
            errors={errors}
          />
        ) : (
          <Text fz="13px" c="dimmed">
            No document requirements are configured for your account. You can
            resubmit using the documents already on file.
          </Text>
        )}
      </Box>
    </Stack>
  );
}

/**
 * Keys of the documents the onboarding setting marks required that are neither
 * already on the contract, nor on the company profile (the API carries those
 * onto the contract on save), nor freshly attached in `documents`. Empty means
 * the customer may resubmit.
 */
export function missingRequiredDocKeys(
  setting: Freight.IFileUploadSetting | undefined,
  contract: Freight.IContract,
  documents: DocumentsValue,
  companyDocs: CompanyDocument[] = [],
): string[] {
  const fields = setting?.fields ?? [];
  const existingCodes = new Set([
    ...(contract.files ?? []).map((f) => f.code),
    ...companyDocs.map((d) => d.code),
  ]);
  return fields
    .filter((f) => f.isRequired)
    .filter(
      (f) => !existingCodes.has(f.fileKey) && !hasFile(documents[f.fileKey]),
    )
    .map((f) => f.fileKey);
}

export { documentSettingCode, hasFile, dedupeLatestByCode };
export default ContractDocsEditor;
