"use client";

import { useActionState, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useFormStatus } from "react-dom";
import { Trophy, TrendingDown } from "lucide-react";
import type { ApiBidPricing, ApiLeaderboardMeta, ApiLeaderboardRow } from "@/lib/api/types";
import Countdown from "@/components/rfq/Countdown";
import { fetchLeaderboard, submitRate, type RespondState } from "./actions";

/**
 * Lives here rather than in `actions.ts`: a "use server" module may only export
 * async functions, so a shared constant would break the build.
 */
const INITIAL_STATE: RespondState = { message: null, errors: {} };

/** How often the board refreshes while the auction runs, in ms. */
const POLL_MS = 6000;

const inr = new Intl.NumberFormat("en-IN", {
  style: "currency",
  currency: "INR",
  maximumFractionDigits: 2,
});

function money(value: number | null | undefined): string {
  if (value === null || value === undefined || !Number.isFinite(value)) return "—";
  return inr.format(value);
}

/** Used when the API sent no terms: the base price is then the Ex price. */
const NO_TERMS: ApiBidPricing = {
  listed_base_price: null,
  gst: 0,
  charges: [],
  charges_total: 0,
  sizes: [{ index: null, label: "All sizes", quantity: 1, gauge_diff: 0 }],
};

/**
 * The legacy seller quotation's estimate (biznie-app `SellerQuotationDetail`
 * and the v1 resource behind it), which is also how the API stores the bid
 * (BidPriceCalculator), so the preview is the number that gets submitted:
 * per size, Ex = (basic + gauge diff + charges) + GST rounded to the rupee,
 * Ex × Qty for the line, and the lines summed to the Total Ex Price.
 */
function priceBid(terms: ApiBidPricing, base: number) {
  const sizes = terms.sizes.map((size) => {
    const taxable = base + size.gauge_diff + terms.charges_total;
    const tax = Math.round((taxable * terms.gst) / 100);
    const ex = taxable + tax;
    return { ...size, taxable, tax, ex, line: ex * size.quantity };
  });
  const quantity = sizes.reduce((sum, s) => sum + s.quantity, 0);
  const total = sizes.reduce((sum, s) => sum + s.line, 0);
  return { sizes, quantity, total };
}

type Props = {
  rfqId: number;
  unit: string;
  /** What is added to the base price; null prices the base price as is. */
  pricing: ApiBidPricing | null;
  /** The base price this seller last bid, when they bid one. */
  quotedBasePrice: number | null;
  exWorksCity: string | null;
  /** The seller's current Ex price, if they have already bid. */
  currentExWorks: number | null;
  /** The legacy "Order Details". */
  dispatchWithinDays: number | null;
  creditDays: number | null;
  /** Biznie's commission, shown when it is included in the seller's price. */
  commission: number | null;
  commissionType: string | null;
  isLive: boolean;
  endsAt: string | null;
  secondsLeft: number;
  /** SSR snapshot so the board paints populated. */
  initialRows: ApiLeaderboardRow[];
  initialMeta: ApiLeaderboardMeta | null;
};

/**
 * Panels 3 and 4 of the seller flow: submit a rate, then watch the board.
 * Laid out like the legacy seller quotation the sellers know: Basic Price in,
 * the Estimate Price table and Order Details worked out from it, and the other
 * sellers' basic prices.
 *
 * They are one component because they share the auction clock and the same
 * refresh — splitting them would mean two pollers disagreeing about whether
 * bidding is still open.
 */
export default function BidPanel({
  rfqId,
  unit,
  pricing,
  quotedBasePrice,
  exWorksCity,
  currentExWorks,
  dispatchWithinDays,
  creditDays,
  commission,
  commissionType,
  isLive,
  endsAt,
  secondsLeft,
  initialRows,
  initialMeta,
}: Props) {
  const [state, formAction] = useActionState(submitRate, INITIAL_STATE);
  const [rows, setRows] = useState(initialRows);
  const [meta, setMeta] = useState(initialMeta);
  const terms = pricing ?? NO_TERMS;
  // Their last bid, else their listing's price — the legacy form pre-filled it
  // too ("Please enter or change current price").
  const startingBase = quotedBasePrice ?? terms.listed_base_price;
  const [draft, setDraft] = useState(startingBase !== null ? String(startingBase) : "");

  const live = meta?.is_live ?? isLive;

  // Read inside the interval so the callback identity stays stable and the
  // timer is not torn down and rebuilt every tick.
  const liveRef = useRef(live);
  liveRef.current = live;

  const refresh = useCallback(async () => {
    const next = await fetchLeaderboard(rfqId);
    if (next) {
      setRows(next.rows);
      setMeta(next.meta);
    }
  }, [rfqId]);

  useEffect(() => {
    if (!isLive) return;

    const timer = setInterval(() => {
      if (!liveRef.current) {
        clearInterval(timer);
        return;
      }
      void refresh();
    }, POLL_MS);

    return () => clearInterval(timer);
  }, [isLive, refresh]);

  // A successful submit should show the seller their new position immediately,
  // not on the next scheduled poll.
  useEffect(() => {
    if (state.message && !Object.keys(state.errors).length) void refresh();
  }, [state, refresh]);

  const handleExpire = useCallback(() => {
    void refresh();
  }, [refresh]);

  // The estimate tracks what the seller is typing, so they see what they are
  // bidding before committing to it.
  const typed = Number(draft);
  const base = draft.trim() !== "" && Number.isFinite(typed) && typed > 0 ? typed : null;
  const estimate = useMemo(() => (base !== null ? priceBid(terms, base) : null), [terms, base]);
  const quantity = terms.sizes.reduce((sum, s) => sum + s.quantity, 0);

  const myRank = meta?.my_rank ?? null;
  const myRow = rows.find((row) => row.is_me);

  return (
    <>
      {/* 3. Submit Your Rate -------------------------------------------- */}
      <div className="rounded-xl bg-white p-5 shadow-sm ring-1 ring-gray-100">
        <div className="mb-4 flex flex-wrap items-center justify-between gap-2">
          <h2 className="text-base font-semibold text-gray-900">
            3. {currentExWorks !== null ? "Improve Your Rate" : "Submit Your Rate"}
          </h2>
          {live && (
            <span className="text-xs font-medium text-gray-500">
              Time Left:{" "}
              <Countdown
                endsAt={endsAt}
                fallbackSeconds={secondsLeft}
                onExpire={handleExpire}
                className="font-semibold"
              />
            </span>
          )}
        </div>

        <form action={formAction} className="space-y-4">
          <input type="hidden" name="id" value={rfqId} />

          <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
            <div>
              <label htmlFor="base-price" className="mb-1 block text-sm font-medium text-gray-700">
                Basic Price (Per {unit}) <span className="text-[#CC1414]">*</span>
              </label>
              <div className="flex items-center gap-2">
                <span className="text-sm text-gray-500">₹</span>
                <input
                  id="base-price"
                  type="number"
                  name="base_price"
                  min={0}
                  step="0.01"
                  inputMode="decimal"
                  value={draft}
                  onChange={(e) => setDraft(e.target.value)}
                  disabled={!live}
                  placeholder="Enter current price"
                  aria-describedby="base-price-hint"
                  className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:border-[#CC1414] focus:outline-none disabled:bg-gray-50"
                />
              </div>
              {state.errors.base_price ? (
                <p className="mt-1 text-xs text-red-600">{state.errors.base_price[0]}</p>
              ) : (
                <p id="base-price-hint" className="mt-1 text-xs text-[#CC1414]">
                  Please enter or change current price.
                  {quotedBasePrice === null && terms.listed_base_price !== null && (
                    <span className="text-gray-500"> Pre-filled from your listing.</span>
                  )}
                </p>
              )}
            </div>

            <div>
              <label htmlFor="bid-remarks" className="mb-1 block text-sm font-medium text-gray-700">
                Write Your Message
              </label>
              <textarea
                id="bid-remarks"
                name="remarks"
                rows={2}
                maxLength={500}
                disabled={!live}
                placeholder="Write your message"
                className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:border-[#CC1414] focus:outline-none disabled:bg-gray-50"
              />
            </div>
          </div>

          {/* Estimate Price: Ex per size, Ex × Qty, Total Ex Price. */}
          <div>
            <p className="mb-1.5 text-xs font-semibold uppercase tracking-wide text-gray-500">
              Estimate Price{exWorksCity ? ` · Ex-Works ${exWorksCity}` : ""}
            </p>
            <div className="overflow-hidden rounded-xl ring-1 ring-gray-200">
              <table className="w-full text-left text-xs sm:text-sm">
                <caption className="sr-only">Ex price for each size at your basic price</caption>
                <thead className="bg-gray-50 text-[11px] text-gray-500 sm:text-xs">
                  <tr>
                    <th scope="col" className="px-2 py-2 font-medium sm:px-3">Requirements</th>
                    <th scope="col" className="px-2 py-2 text-right font-medium sm:px-3">Ex Price</th>
                    <th scope="col" className="px-2 py-2 text-right font-medium sm:px-3">Ex Price × Qty</th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-gray-100 tabular-nums">
                  {terms.sizes.map((size, i) => {
                    const priced = estimate?.sizes[i];
                    return (
                      <tr key={`${size.label}-${i}`}>
                        <td className="px-2 py-2 text-gray-900 sm:px-3">
                          {size.label}
                          <span className="block text-[11px] text-gray-500 sm:inline sm:text-sm sm:text-gray-900">
                            <span className="hidden sm:inline"> – </span>
                            {size.quantity} {unit}
                          </span>
                        </td>
                        <td className="whitespace-nowrap px-2 py-2 text-right text-gray-700 sm:px-3">
                          {priced ? (
                            <>
                              {money(priced.ex)}
                              <span className="hidden text-xs text-gray-400 sm:inline">/{unit}</span>
                            </>
                          ) : (
                            "—"
                          )}
                        </td>
                        <td className="whitespace-nowrap px-2 py-2 text-right text-gray-900 sm:px-3">
                          {priced ? money(priced.line) : "—"}
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
                <tfoot className="bg-emerald-50">
                  <tr>
                    <th scope="row" colSpan={2} className="px-2 py-2.5 text-left text-sm font-semibold text-emerald-900 sm:px-3">
                      Total Ex Price
                    </th>
                    <td className="whitespace-nowrap px-2 py-2.5 text-right text-sm font-bold tabular-nums text-emerald-700 sm:px-3 sm:text-base">
                      {estimate ? money(estimate.total) : "—"}
                    </td>
                  </tr>
                </tfoot>
              </table>
            </div>

            {/* What turns the basic price into each Ex price. */}
            <details className="mt-2 rounded-lg bg-gray-50 px-3 py-2 text-xs text-gray-600 ring-1 ring-gray-100">
              <summary className="cursor-pointer font-medium text-gray-700">How the Ex price is worked out</summary>
              <p className="mt-2">
                Ex Price = Basic Price + Gauge Difference
                {terms.charges.length > 0 ? " + charges" : ""} + GST ({terms.gst}%)
              </p>
              <dl className="mt-2 space-y-1">
                {terms.sizes.some((s) => s.gauge_diff !== 0) && (
                  <BreakupRow
                    label="Gauge Difference"
                    value={terms.sizes.map((s) => `${s.label}: ${money(s.gauge_diff)}`).join(" · ")}
                  />
                )}
                {terms.charges.map((charge) => (
                  <BreakupRow key={charge.label} label={charge.label} value={money(charge.amount)} />
                ))}
                <BreakupRow label="GST" value={`${terms.gst}% on the above`} />
              </dl>
            </details>
          </div>

          {/* Order Details, as on the legacy quotation. */}
          <div className="rounded-xl bg-gradient-to-br from-red-50 to-amber-50 p-4 ring-1 ring-red-100">
            <p className="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-500">Order Details</p>
            <dl className="space-y-1.5 text-sm">
              <div className="flex items-center justify-between gap-3">
                <dt className="text-gray-600">Basic Price</dt>
                <dd className="text-xl font-bold tabular-nums text-emerald-700">{base !== null ? money(base) : "—"}</dd>
              </div>
              <BreakupRow label="Total Quantity" value={`${quantity} ${unit}`} />
              {dispatchWithinDays ? <BreakupRow label="Dispatch Within" value={`${dispatchWithinDays} Days`} /> : null}
              {creditDays ? <BreakupRow label="Credit Days" value={`${creditDays} Days`} /> : null}
              {commissionType === "include" && commission ? (
                <BreakupRow
                  label="Commission (Included)"
                  value={`${money(commission * quantity)} (${money(commission)} / ${unit}) + ${terms.gst}% GST`}
                />
              ) : null}
            </dl>
            <p className="mt-2 text-xs text-gray-500">
              Seller shall be fully responsible for the quality of goods supplied.
            </p>
          </div>

          <div className="flex flex-wrap items-center justify-between gap-2">
            <span
              className={`text-sm ${
                state.message && /fail|closed|error|not/i.test(state.message)
                  ? "text-red-600"
                  : "text-emerald-700"
              }`}
              role="status"
            >
              {state.message}
            </span>
            <SubmitButton live={live} hasBid={currentExWorks !== null} />
          </div>
        </form>
      </div>

      {/* 4. Live bidding ------------------------------------------------- */}
      <div className="rounded-xl bg-white p-5 shadow-sm ring-1 ring-gray-100">
        <div className="mb-1 flex flex-wrap items-center justify-between gap-2">
          <h2 className="text-base font-semibold text-gray-900">4. Live Bidding — Other Sellers&apos; Basic Price</h2>
          {live && (
            <span className="text-xs font-medium text-gray-500">
              Time Left:{" "}
              <Countdown endsAt={endsAt} fallbackSeconds={secondsLeft} className="font-semibold" />
            </span>
          )}
        </div>
        <p className="mb-4 text-xs font-medium text-[#CC1414]">Preference will be given to the lowest price.</p>

        {myRank !== null && (
          <div className="mb-4 grid grid-cols-2 gap-4 rounded-xl bg-emerald-50 p-4 ring-1 ring-emerald-200">
            <div>
              <p className="text-xs text-emerald-800">Your Current Rank</p>
              <p className="flex items-center gap-1.5 text-2xl font-bold text-emerald-700">
                {myRank === 1 && <Trophy className="h-5 w-5" />}#{myRank}
                <span className="text-sm font-normal text-emerald-800">of {meta?.total_bids ?? rows.length}</span>
              </p>
            </div>
            <div className="text-right">
              <p className="text-xs text-emerald-800">Your Basic Price</p>
              <p className="text-2xl font-bold text-emerald-700">{money(myRow?.base_price ?? quotedBasePrice)}</p>
              {myRow && <p className="text-xs text-emerald-800">Ex {money(myRow.ex_works_price)}/{unit}</p>}
            </div>
          </div>
        )}

        {rows.length === 0 ? (
          <p className="rounded-lg bg-gray-50 p-6 text-center text-sm text-gray-500">
            No rates submitted yet. Be the first to bid.
          </p>
        ) : (
          <table className="w-full divide-y divide-gray-100 text-sm">
            <caption className="sr-only">Anonymised bids on this RFQ, lowest price first</caption>
            <thead className="bg-gray-50 text-left text-xs font-semibold uppercase tracking-wide text-gray-500">
              <tr>
                <th scope="col" className="px-2 py-2 sm:px-3">Rank</th>
                <th scope="col" className="px-2 py-2 sm:px-3">Seller</th>
                <th scope="col" className="whitespace-nowrap px-2 py-2 text-right sm:px-3">Basic Price</th>
                <th scope="col" className="whitespace-nowrap px-2 py-2 text-right sm:px-3">
                  Ex Price<span className="hidden sm:inline">/{unit}</span>
                </th>
              </tr>
            </thead>
            <tbody className="divide-y divide-gray-100">
              {rows.map((row, i) => (
                <tr key={row.id} className={row.is_me ? "bg-emerald-50/60 font-medium" : ""}>
                  <td className="px-2 py-2 sm:px-3">{row.rank}</td>
                  <td className="px-2 py-2 text-gray-900 sm:px-3">
                    <span className="whitespace-nowrap">Seller {i + 1}</span>
                    {row.is_me && (
                      <span className="mt-0.5 block w-fit whitespace-nowrap rounded-full bg-[#CC1414] px-2 py-0.5 text-[10px] font-semibold text-white sm:ml-2 sm:mt-0 sm:inline-block">
                        My Offer
                      </span>
                    )}
                  </td>
                  <td className="whitespace-nowrap px-2 py-2 text-right font-semibold tabular-nums text-gray-900 sm:px-3">
                    {money(row.base_price)}
                  </td>
                  <td className="whitespace-nowrap px-2 py-2 text-right tabular-nums text-gray-600 sm:px-3">
                    {money(row.ex_works_price)}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}

        <p className="mt-3 flex items-center gap-1.5 text-xs text-gray-500">
          <TrendingDown className="h-3.5 w-3.5" />
          {live
            ? "Keep improving your price to win the order. Competing sellers are not identified."
            : "Bidding has closed for this RFQ."}
        </p>
      </div>
    </>
  );
}

function BreakupRow({ label, value }: { label: string; value: string }) {
  return (
    <div className="flex items-start justify-between gap-3">
      <dt className="text-gray-600">{label}</dt>
      <dd className="text-right font-medium tabular-nums text-gray-900">{value}</dd>
    </div>
  );
}

/**
 * Nested so `useFormStatus` has a form to read from — the hook only reports on a
 * form above the component calling it.
 */
function SubmitButton({ live, hasBid }: { live: boolean; hasBid: boolean }) {
  const { pending } = useFormStatus();

  return (
    <button
      type="submit"
      disabled={pending || !live}
      className="rounded-lg bg-[#CC1414] px-5 py-2.5 text-sm font-medium text-white hover:bg-[#a30f0f] disabled:opacity-60"
    >
      {!live ? "Bidding closed" : pending ? "Submitting…" : hasBid ? "Improve Your Rate" : "Submit"}
    </button>
  );
}
