import { useMemo, useRef, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
  CheckCircle2,
  Clock,
  FileText,
  RefreshCw,
  Save,
  Trash2,
  Undo2,
  UploadCloud,
  UserCheck,
  XCircle,
} from "lucide-react";
import {
  ActionIcon,
  Alert,
  Anchor,
  Badge,
  Button,
  Card,
  Group,
  Loader,
  Stack,
  Title,
  Text,
  TextInput,
  Tooltip,
  Grid,
} from "@mantine/core";
import { useFileViewer, type ViewableFile } from "@edr/ui-common";
import { api } from "@/services/api";
import { fetchViewableFile } from "@/services/files.service";
import {
  companiesService,
  type LicenseFile,
  type LicenseFileStatus,
} from "@/services/companies.service";
import { ControlledPhoneField, isValidPhone } from "@/components/PhoneField";
import type { ProfileResponse } from "@/types/profile";

const schema = z.object({
  poaName: z.string().optional(),
  poaEmail: z.string().optional(),
  poaPhone: z
    .string()
    .optional()
    .refine((v) => !v || isValidPhone(v), "Enter a valid phone number"),
  poaLocation: z.string().optional(),
  poaAddress: z.string().optional(),
});

type FormData = z.infer<typeof schema>;

const LETTER_ACCEPT = ".pdf,.png,.jpg,.jpeg";

const STATUS_BADGE: Record<
  LicenseFileStatus,
  { label: string; bg: string; fg: string } | null
> = {
  live: null,
  pending_add: {
    label: "Pending approval",
    bg: "var(--mantine-color-edr-amber-soft-0)",
    fg: "var(--mantine-color-edr-amber-text-0)",
  },
  pending_remove: {
    label: "Removal pending",
    bg: "var(--mantine-color-edr-red-soft-0)",
    fg: "var(--mantine-color-edr-red-0)",
  },
};

function formatBytes(bytes: number): string {
  if (!bytes) return "";
  const units = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(1024));
  return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(1))} ${units[i]}`;
}

interface TabPowerOfAttorneyProps {
  profile: ProfileResponse;
  mode?: "edit" | "onboarding";
  onContinue?: () => void;
}

export default function TabPowerOfAttorney({
  profile,
  mode = "edit",
  onContinue,
}: TabPowerOfAttorneyProps) {
  const queryClient = useQueryClient();
  const { view, viewer } = useFileViewer();
  const uploadInputRef = useRef<HTMLInputElement>(null);

  const defaultValues = useMemo((): FormData => {
    return {
      poaName: profile.poaName ?? "",
      poaEmail: profile.poaEmail ?? "",
      poaPhone: profile.poaPhone ?? "",
      poaLocation: profile.poaLocation ?? "",
      poaAddress: profile.poaAddress ?? "",
    };
  }, [profile]);

  const {
    register,
    control,
    handleSubmit,
    reset,
    watch,
    formState: { errors, isDirty },
  } = useForm<FormData>({
    resolver: zodResolver(schema),
    values: defaultValues,
  });

  const letterQuery = useQuery(api.companies.poaDelegation.queryOptions({}));
  const letters = useMemo(() => letterQuery.data ?? [], [letterQuery.data]);

  // The letter is staged locally, not uploaded on pick. Uploading immediately
  // would open a change request, which locks the whole settings page (see
  // SettingsPage's `locked` fieldset) before the text fields could be saved.
  // Save submits the file and the fields together, into one change request.
  const [pickedFile, setPickedFile] = useState<File | null>(null);
  const [removeIds, setRemoveIds] = useState<string[]>([]);
  const [saveBlocked, setSaveBlocked] = useState(false);

  /** Letters that will still be on file once the staged edits are applied. */
  const remainingLetters = letters.filter(
    (f) => f.status !== "pending_remove" && !removeIds.includes(f.id),
  );
  const hasLetterAfterSave = Boolean(pickedFile) || remainingLetters.length > 0;

  // A freight forwarder signs on other companies' behalf, so its PoA — details
  // and delegation letter both — is mandatory rather than optional.
  const requirePoa = profile.companyProfiles.some(
    (p) => p.type === "freight_forwarder",
  );
  const poaValues = watch([
    "poaName",
    "poaEmail",
    "poaPhone",
    "poaLocation",
    "poaAddress",
  ]);
  const poaProvided = poaValues.some((v) => v?.trim());
  const letterRequired = requirePoa || poaProvided;
  const letterMissing = letterRequired && !hasLetterAfterSave;

  const fileDirty = Boolean(pickedFile) || removeIds.length > 0;

  const mutation = useMutation({
    mutationFn: async (data: FormData) => {
      // A fresh upload already stages the removal of every live letter, so the
      // explicit removals only need applying when no replacement was picked.
      if (pickedFile) {
        await companiesService.uploadPoaDelegation(pickedFile);
      } else {
        for (const fileId of removeIds) {
          await companiesService.removePoaDelegation(fileId);
        }
      }
      return api.companies.updateProfile.call({
        poaName: data.poaName || undefined,
        poaPhone: data.poaPhone || undefined,
        poaEmail: data.poaEmail || undefined,
        poaLocation: data.poaLocation || undefined,
        poaAddress: data.poaAddress || undefined,
      });
    },
    onSuccess: () => {
      setPickedFile(null);
      setRemoveIds([]);
      queryClient.invalidateQueries({
        queryKey: api.companies.poaDelegation.queryKey(),
      });
      queryClient.invalidateQueries({
        queryKey: api.companies.getProfile.queryKey(),
      });
      if (mode === "onboarding") onContinue?.();
    },
  });

  const onSubmit = (data: FormData) => {
    // The letter lives outside the form state, so it's gated here rather than
    // in the zod resolver.
    if (letterMissing) {
      setSaveBlocked(true);
      return;
    }
    setSaveBlocked(false);
    mutation.mutate(data);
  };

  const resetAll = () => {
    reset();
    setPickedFile(null);
    setRemoveIds([]);
    setSaveBlocked(false);
  };

  const pickFile = (file: File) => {
    setSaveBlocked(false);
    setPickedFile(file);
    // An upload already supersedes every letter on file, so a pending explicit
    // removal would be a no-op — drop it rather than mislabel the row.
    setRemoveIds([]);
  };

  const toggleRemove = (fileId: string) => {
    setSaveBlocked(false);
    setRemoveIds((prev) =>
      prev.includes(fileId)
        ? prev.filter((id) => id !== fileId)
        : [...prev, fileId],
    );
  };

  return (
    <>
      <Card padding="lg">
        <Group gap="sm" mb="xs">
          <UserCheck size={20} />
          <Title order={3}>Power of Attorney</Title>
          {requirePoa && (
            <Badge size="sm" variant="light" color="blue">
              Required for freight forwarder
            </Badge>
          )}
        </Group>
        <Text c="edr-muted" size="sm" mb="lg">
          {requirePoa
            ? "As a freight forwarder you act on other companies' behalf, so a Power of Attorney and its delegation letter are required."
            : "Power of Attorney details are optional. If you name a representative, upload the delegation letter authorising them."}
        </Text>

        <form onSubmit={handleSubmit(onSubmit)}>
          <Stack gap="md">
            <TextInput
              label="PoA Full Name"
              placeholder="Authorized Representative Name"
              error={errors.poaName?.message}
              {...register("poaName")}
            />

            <Grid>
              <Grid.Col span={6}>
                <TextInput
                  label="PoA Email"
                  type="email"
                  placeholder="poa@company.com"
                  error={errors.poaEmail?.message}
                  {...register("poaEmail")}
                />
              </Grid.Col>
              <Grid.Col span={6}>
                <ControlledPhoneField
                  control={control}
                  name="poaPhone"
                  label="PoA Phone"
                />
              </Grid.Col>
            </Grid>

            <Grid>
              <Grid.Col span={6}>
                <TextInput
                  label="PoA Location"
                  placeholder="City, Country"
                  error={errors.poaLocation?.message}
                  {...register("poaLocation")}
                />
              </Grid.Col>
              <Grid.Col span={6}>
                <TextInput
                  label="PoA Address"
                  placeholder="Full Address"
                  error={errors.poaAddress?.message}
                  {...register("poaAddress")}
                />
              </Grid.Col>
            </Grid>
          </Stack>

          {/* ------------------------ Delegation letter ------------------------ */}
          <Stack gap="sm" mt="xl">
            <Group justify="space-between" align="center">
              <Group gap="sm">
                <FileText size={18} />
                <Text fw={600} c="edr-text">
                  Delegation letter
                </Text>
              </Group>
              <Button
                type="button"
                variant="light"
                size="xs"
                leftSection={
                  hasLetterAfterSave ? (
                    <RefreshCw size={14} />
                  ) : (
                    <UploadCloud size={14} />
                  )
                }
                disabled={mutation.isPending}
                onClick={() => uploadInputRef.current?.click()}
              >
                {hasLetterAfterSave ? "Replace letter" : "Upload letter"}
              </Button>
            </Group>

            <Text c="edr-muted" size="xs">
              The signed letter in which the General Manager delegates the
              representative above. Submitted to EDR for review together with the
              details; it takes effect once approved.
            </Text>

            {saveBlocked && letterMissing && (
              <Alert
                color="red"
                variant="light"
                icon={<XCircle size={18} />}
              >
                {requirePoa
                  ? "Upload the delegation letter before saving — it is required for freight forwarders."
                  : "Upload the delegation letter for the representative you named, or clear the PoA details."}
              </Alert>
            )}

            {letterQuery.isLoading ? (
              <Group justify="center" py="md">
                <Loader size="sm" color="edr-green" />
              </Group>
            ) : letters.length === 0 && !pickedFile ? (
              <Card
                padding="md"
                radius="md"
                style={{
                  borderStyle: "dashed",
                  backgroundColor: "var(--mantine-color-edr-bg-0)",
                }}
              >
                <Text size="sm" c="edr-muted" ta="center">
                  No delegation letter uploaded.
                </Text>
              </Card>
            ) : (
              <Stack gap="xs">
                {letters.map((f) => (
                  <LetterRow
                    key={f.id}
                    file={f}
                    markedForRemoval={removeIds.includes(f.id)}
                    supersededBy={pickedFile}
                    disabled={mutation.isPending}
                    onToggleRemove={() => toggleRemove(f.id)}
                    onViewFile={view}
                  />
                ))}
                {pickedFile && (
                  <Card
                    padding="sm"
                    radius="md"
                    withBorder
                    style={{
                      backgroundColor: "var(--mantine-color-edr-card-0)",
                    }}
                  >
                    <Group gap="sm" wrap="nowrap">
                      <FileText
                        size={18}
                        className="text-edr-muted"
                        style={{ flexShrink: 0 }}
                      />
                      <Stack gap={0} style={{ minWidth: 0, flex: 1 }}>
                        <Text size="sm" fw={600} c="edr-text" lineClamp={1}>
                          {pickedFile.name}
                        </Text>
                        <Text size="xs" c="edr-muted">
                          {formatBytes(pickedFile.size)}
                        </Text>
                      </Stack>
                      <Badge
                        size="sm"
                        radius="sm"
                        variant="light"
                        color="blue"
                        style={{ flexShrink: 0 }}
                      >
                        Submitted on save
                      </Badge>
                      <Tooltip label="Discard" withArrow>
                        <ActionIcon
                          variant="subtle"
                          color="red"
                          aria-label="Discard selected letter"
                          disabled={mutation.isPending}
                          onClick={() => setPickedFile(null)}
                        >
                          <Trash2 size={15} />
                        </ActionIcon>
                      </Tooltip>
                    </Group>
                  </Card>
                )}
              </Stack>
            )}

            {profile.reviewStatus === "pending" && (
              <Group gap={6} c="edr-amber-text">
                <Clock size={13} />
                <Text size="xs" fw={500}>
                  Awaiting EDR review — this letter takes effect once approved.
                </Text>
              </Group>
            )}
            <input
              ref={uploadInputRef}
              type="file"
              accept={LETTER_ACCEPT}
              style={{ display: "none" }}
              onChange={(e) => {
                const file = e.target.files?.[0];
                if (file) pickFile(file);
                e.target.value = "";
              }}
            />
          </Stack>

          <Group
            justify="space-between"
            mt="xl"
            pt="md"
            style={{ borderTop: "1px solid var(--mantine-color-edr-border-0)" }}
          >
            <Group gap="xs">
              {mutation.isSuccess && (
                <Group gap={6} c="green">
                  <CheckCircle2 size={16} />
                  <Text size="sm" fw={500}>
                    Saved successfully
                  </Text>
                </Group>
              )}
              {mutation.isError && (
                <Group gap={6} c="red">
                  <XCircle size={16} />
                  <Text size="sm" fw={500}>
                    Save failed
                  </Text>
                </Group>
              )}
            </Group>
            <Group gap="md">
              {mode === "edit" && (
                <Button
                  type="button"
                  variant="outline"
                  disabled={mutation.isPending || (!isDirty && !fileDirty)}
                  onClick={resetAll}
                >
                  Reset
                </Button>
              )}
              <Button
                type="submit"
                leftSection={<Save size={16} />}
                loading={mutation.isPending}
              >
                {mode === "onboarding" ? "Continue" : "Save Changes"}
              </Button>
            </Group>
          </Group>
        </form>
      </Card>

      {viewer}
    </>
  );
}

/**
 * One letter already on file. `pending_add` / `pending_remove` reflect a change
 * request the backoffice hasn't ruled on yet; `markedForRemoval` and
 * `supersededBy` are this session's unsaved edits.
 */
function LetterRow({
  file,
  markedForRemoval,
  supersededBy,
  disabled,
  onToggleRemove,
  onViewFile,
}: {
  file: LicenseFile;
  markedForRemoval: boolean;
  supersededBy: File | null;
  disabled: boolean;
  onToggleRemove: () => void;
  onViewFile: (file: ViewableFile) => void;
}) {
  const badge = STATUS_BADGE[file.status];
  const superseded = Boolean(supersededBy) && file.status !== "pending_remove";
  const struck =
    file.status === "pending_remove" || markedForRemoval || superseded;

  return (
    <Card
      padding="sm"
      radius="md"
      withBorder
      style={{ backgroundColor: "var(--mantine-color-edr-card-0)" }}
    >
      <Group gap="sm" wrap="nowrap">
        <FileText
          size={18}
          className="text-edr-muted"
          style={{ flexShrink: 0 }}
        />
        <Stack gap={0} style={{ minWidth: 0, flex: 1 }}>
          <Anchor
            component="button"
            type="button"
            size="sm"
            fw={600}
            onClick={() =>
              void fetchViewableFile(file.id, file.name).then(onViewFile)
            }
            style={{
              textAlign: "left",
              textDecoration: struck ? "line-through" : undefined,
            }}
            lineClamp={1}
          >
            {file.name}
          </Anchor>
          {file.size > 0 && (
            <Text size="xs" c="edr-muted">
              {formatBytes(file.size)}
            </Text>
          )}
        </Stack>

        {badge && (
          <Badge
            size="sm"
            radius="sm"
            variant="light"
            leftSection={<Clock size={11} />}
            style={{ backgroundColor: badge.bg, color: badge.fg, flexShrink: 0 }}
          >
            {badge.label}
          </Badge>
        )}
        {superseded && !markedForRemoval && (
          <Badge
            size="sm"
            radius="sm"
            variant="light"
            color="gray"
            style={{ flexShrink: 0 }}
          >
            Replaced on save
          </Badge>
        )}
        {markedForRemoval && (
          <Badge
            size="sm"
            radius="sm"
            variant="light"
            color="red"
            style={{ flexShrink: 0 }}
          >
            Removed on save
          </Badge>
        )}

        {file.status !== "pending_remove" && !superseded && (
          <Tooltip
            label={markedForRemoval ? "Keep" : "Remove"}
            withArrow
          >
            <ActionIcon
              variant="subtle"
              color={markedForRemoval ? "gray" : "red"}
              aria-label={
                markedForRemoval ? `Keep ${file.name}` : `Remove ${file.name}`
              }
              disabled={disabled}
              onClick={onToggleRemove}
            >
              {markedForRemoval ? <Undo2 size={15} /> : <Trash2 size={15} />}
            </ActionIcon>
          </Tooltip>
        )}
      </Group>
    </Card>
  );
}
