"use client";

import { useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { Globe2 } from "lucide-react";
import { ErrorState } from "@/components/shared/states";
import { StatusBadge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { FormField } from "@/components/ui/form-field";
import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import { Switch } from "@/components/ui/switch";
import { useCan } from "@/features/auth/use-can";
import { useI18n, useT } from "@/i18n";
import { friendlyErrorMessage } from "@/lib/errors";
import { formatCurrency } from "@/lib/format";
import { queryKeys } from "@/lib/query-keys";
import { useFieldErrors } from "@/lib/use-field-errors";
import {
  getDefaultDeliveryRate,
  updateDefaultDeliveryRate,
} from "@/services/cities";
import type { DefaultDeliveryRate } from "@/types/city";

/**
 * What a delivery is priced at when its drop-off is outside every area below.
 *
 * It sits above the areas because it is the answer for everywhere they do not reach: the same one number an
 * area carries — what the shop is billed and what the captain is paid — and the same freelancer split. Off,
 * such a delivery keeps the fee the shop sent and earns the captain nothing until somebody maps the place.
 */
export function DefaultRateCard({ currency }: { currency: string }) {
  const t = useT();
  const { locale } = useI18n();
  const { can } = useCan();
  const canRead = can("delivery_fees.view");
  const canManage = can("delivery_fees.manage");

  const { data, isLoading, isError, error, refetch } = useQuery({
    queryKey: queryKeys.cities.defaultRate,
    queryFn: async () => (await getDefaultDeliveryRate()).Model ?? null,
    enabled: canRead,
  });

  if (!canRead) return null;

  return (
    <Card>
      <div className="flex flex-wrap items-start justify-between gap-3 border-b p-4">
        <div className="flex items-start gap-2">
          <Globe2
            className="mt-0.5 h-4 w-4 shrink-0 text-primary"
            aria-hidden
          />
          <div>
            <p className="text-sm font-medium">{t("defaultRate.title")}</p>
            <p className="mt-1 text-xs leading-5 text-muted-foreground">
              {t("defaultRate.hint")}
            </p>
          </div>
        </div>
        {data ? (
          data.applies ? (
            <StatusBadge variant="success">{t("defaultRate.on")}</StatusBadge>
          ) : (
            <StatusBadge variant="secondary">
              {t("defaultRate.off")}
            </StatusBadge>
          )
        ) : null}
      </div>

      {isLoading ? (
        <div className="space-y-2 p-4">
          <Skeleton className="h-9 w-full" />
          <Skeleton className="h-9 w-full" />
        </div>
      ) : isError || !data ? (
        <ErrorState
          message={friendlyErrorMessage(error, t)}
          onRetry={() => refetch()}
        />
      ) : !canManage ? (
        <p className="p-4 text-sm">
          {data.applies
            ? t("defaultRate.readOnly", {
                amount: formatCurrency(data.delivery_earning, currency, locale),
                share: String(Math.round(data.freelance_share * 10000) / 100),
              })
            : t("defaultRate.offExplained")}
        </p>
      ) : (
        // Keyed on the save, so the form starts again from what the server now holds.
        <DefaultRateForm
          key={data.updated_at ?? "unset"}
          rate={data}
          currency={currency}
        />
      )}
    </Card>
  );
}

/**
 * The editing form, started from the saved rate. The amount and the freelancer's share are typed as on an area —
 * the share as a percentage, sent as a fraction.
 */
function DefaultRateForm({
  rate,
  currency,
}: {
  rate: DefaultDeliveryRate;
  currency: string;
}) {
  const t = useT();
  const { locale } = useI18n();
  const queryClient = useQueryClient();

  const [amount, setAmount] = useState(
    rate.delivery_earning > 0 ? String(rate.delivery_earning) : "",
  );
  const [share, setShare] = useState(
    String(Math.round(rate.freelance_share * 10000) / 100),
  );
  const [active, setActive] = useState(rate.is_active);

  const fields = useFieldErrors({
    only: ["delivery_earning", "freelance_share", "is_active"],
  });

  const numeric = (value: string) =>
    value.trim() === "" ? Number.NaN : Number(value);
  const amountValue = numeric(amount);
  const shareValue = numeric(share);

  const invalid =
    Number.isNaN(shareValue) ||
    shareValue < 0 ||
    shareValue > 100 ||
    (amount.trim() !== "" && (Number.isNaN(amountValue) || amountValue < 0)) ||
    // Switched on at nothing would price every delivery outside the areas at zero. The server refuses it too.
    (active && !(amountValue > 0));

  const save = useMutation({
    mutationFn: () =>
      updateDefaultDeliveryRate({
        delivery_earning: Number.isNaN(amountValue) ? 0 : amountValue,
        freelance_share: shareValue / 100,
        is_active: active,
      }),
    onMutate: fields.reset,
    onSuccess: (response) => {
      toast.success(response.Message ?? t("defaultRate.saved"));
      void queryClient.invalidateQueries({ queryKey: queryKeys.cities.all });
    },
    onError: (err) => {
      if (!fields.capture(err)) toast.error(friendlyErrorMessage(err, t));
    },
  });

  return (
    <form
      className="space-y-4 p-4"
      onSubmit={(event) => {
        event.preventDefault();
        if (invalid || save.isPending) return;
        save.mutate();
      }}
    >
      <div className="grid gap-4 sm:grid-cols-2">
        <FormField
          label={t("defaultRate.amount")}
          htmlFor="default-rate-amount"
          error={fields.get("delivery_earning")}
          hint={t("defaultRate.amountHint")}
        >
          <div className="flex items-center gap-2">
            <Input
              id="default-rate-amount"
              type="number"
              min="0"
              step="any"
              className="numeric"
              value={amount}
              onChange={(event) => setAmount(event.target.value)}
              aria-invalid={fields.get("delivery_earning") !== undefined}
            />
            <span
              className="w-10 shrink-0 text-sm text-muted-foreground"
              dir="ltr"
            >
              {currency}
            </span>
          </div>
        </FormField>

        <FormField
          label={t("cities.freelanceShare")}
          htmlFor="default-rate-share"
          error={fields.get("freelance_share")}
          hint={t("cities.freelanceShareHint", {
            amount: formatCurrency(
              Number.isNaN(amountValue) || Number.isNaN(shareValue)
                ? 0
                : (amountValue * shareValue) / 100,
              currency,
              locale,
            ),
          })}
        >
          <div className="flex items-center gap-2">
            <Input
              id="default-rate-share"
              type="number"
              min="0"
              max="100"
              step="any"
              className="numeric"
              value={share}
              onChange={(event) => setShare(event.target.value)}
              aria-invalid={fields.get("freelance_share") !== undefined}
            />
            <span
              className="w-10 shrink-0 text-sm text-muted-foreground"
              dir="ltr"
            >
              %
            </span>
          </div>
        </FormField>
      </div>

      <div className="flex items-center justify-between gap-4 rounded-lg border p-4">
        <span className="text-sm">
          <span className="font-medium">{t("defaultRate.active")}</span>
          <span className="mt-1 block text-xs leading-5 text-muted-foreground">
            {!active
              ? t("defaultRate.activeHint")
              : amountValue > 0
                ? t("defaultRate.onHint")
                : t("defaultRate.needsAmount")}
          </span>
        </span>
        <Switch
          checked={active}
          onCheckedChange={setActive}
          aria-label={t("defaultRate.active")}
        />
      </div>

      <div className="flex flex-wrap items-center justify-between gap-3">
        <p className="text-xs text-muted-foreground">
          {rate.updated_at
            ? t("defaultRate.lastChanged", {
                at: rate.updated_at,
                by: rate.updated_by ?? "—",
              })
            : null}
        </p>
        <Button
          type="submit"
          size="sm"
          loading={save.isPending}
          disabled={invalid || save.isPending}
        >
          {save.isPending ? t("common.saving") : t("common.save")}
        </Button>
      </div>
    </form>
  );
}
