'use client'

import { useCallback, useEffect, useMemo, useState } from 'react'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { stateName } from '@/lib/domain/states'
import { formatCents, productCodeForRouteType, type RouteType as ProductRouteType } from '@/lib/domain/routing-products'

/**
 * Buying a route — prepaid credits vs. a shopping cart (2026-09-07, Task 69).
 *
 * Nash: "if I have an active balance of prepaid express routes… there's no
 * need for a shopping cart. I should have a button to [get] the express route,
 * and it should show me there that I have 15 available. When I buy one, it's
 * gonna go drop to 14, to 13."
 *
 * Nash: "just a one click with an alert, it's not enough, because this is a
 * money transaction. If I need to pay for it, it should go into a shopping
 * cart, and then I should pay for the items."
 *
 * ONE implementation for the desktop workspace permit card and the driver's
 * Active Trip route row — "the same flow, the same logic".
 *
 * 2026-09-29 (Synchron purchasing task, D3): the cart lives in the database
 * (`trip_carts` / `trip_cart_items`, one open cart per user per trip) behind
 * `/api/trips/[id]/cart`. Prices come ONLY from the server; the browser sends
 * a `product_code` and displays whatever `GET …/cart` returns.
 */

export interface RouteCredits {
  planName: string
  included: number
  used: number
  remaining: number
}

export type RouteType = ProductRouteType

/** One priced line of the server-side cart. */
export interface CartItem {
  id: string
  permitId: string
  stateCode: string | null
  productCode: string
  productName: string
  routeType: RouteType
  unitPriceCents: number
  lineTotalCents: number
  /** Server-side note (permit already has a route / open request) — never blocks. */
  warning: string | null
}

const dollars = (cents: number) => `$${(cents / 100).toFixed(2)}`

/* ---------------- Routing products (prices from the server) ---------------- */

export interface RoutingProductOption {
  product_code: string
  product_name: string
  description: string | null
  route_type: RouteType
  unit_price_cents: number
  currency: string
  product_version: number
}

// Fetched once per page load and shared by every button on the page.
let productsCache: RoutingProductOption[] | null = null
let productsPromise: Promise<RoutingProductOption[]> | null = null

async function fetchRoutingProducts(): Promise<RoutingProductOption[]> {
  if (productsCache) return productsCache
  if (!productsPromise) {
    productsPromise = fetch('/api/routing-products')
      .then(async (res) => {
        const json = await res.json().catch(() => ({}))
        if (!res.ok || !Array.isArray(json.products)) throw new Error(json.error ?? 'Could not load routing products')
        productsCache = json.products as RoutingProductOption[]
        return productsCache
      })
      .catch((e) => {
        productsPromise = null
        throw e
      })
  }
  return productsPromise
}

export function useRoutingProducts(): { products: RoutingProductOption[]; loading: boolean; error: string | null } {
  const [products, setProducts] = useState<RoutingProductOption[]>(productsCache ?? [])
  const [loading, setLoading] = useState(!productsCache)
  const [error, setError] = useState<string | null>(null)
  useEffect(() => {
    let alive = true
    fetchRoutingProducts()
      .then((p) => {
        if (!alive) return
        setProducts(p)
        setError(null)
      })
      .catch((e: unknown) => alive && setError(e instanceof Error ? e.message : 'Could not load routing products'))
      .finally(() => alive && setLoading(false))
    return () => {
      alive = false
    }
  }, [])
  return { products, loading, error }
}

/* ---------------- Cart: per user, per trip (server-side) ---------------- */

interface PricedCartLineWire {
  id: string
  permit_id: string
  state_code: string | null
  product_code: string
  product_name: string
  route_type: RouteType
  quantity: number
  unit_price_cents: number
  line_total_cents: number
  currency: string
  warning: string | null
}

interface PricedCartWire {
  items: PricedCartLineWire[]
  subtotal_cents: number
  total_cents: number
  currency: string
}

function toItem(line: PricedCartLineWire): CartItem {
  return {
    id: line.id,
    permitId: line.permit_id,
    stateCode: line.state_code,
    productCode: line.product_code,
    productName: line.product_name,
    routeType: line.route_type,
    unitPriceCents: line.unit_price_cents,
    lineTotalCents: line.line_total_cents,
    warning: line.warning,
  }
}

export interface RouteCart {
  items: CartItem[]
  loading: boolean
  has: (permitId: string) => boolean
  /** Resolves true when the line is in the cart (errors are toasted). */
  add: (item: { permitId: string; productCode: string; quantity?: number }) => Promise<boolean>
  remove: (permitId: string) => Promise<void>
  clear: () => Promise<void>
  refresh: () => Promise<void>
  totalCents: number
  subtotalCents: number
  currency: string
}

const EMPTY_CART: PricedCartWire = { items: [], subtotal_cents: 0, total_cents: 0, currency: 'USD' }

/**
 * The cart belongs to one user on one trip (routes belong to a trip; a cart
 * shared across trips would mix orders for different loads). It lives in the
 * database, so it survives a reload and navigation. Mutations are optimistic
 * and every response from the server replaces the local copy.
 */
export function useRouteCart(tripId: string, userId: string, opts: { enabled?: boolean } = {}): RouteCart {
  const enabled = (opts.enabled ?? true) && !!tripId && tripId !== 'none' && !!userId
  const [wire, setWire] = useState<PricedCartWire>(EMPTY_CART)
  const [loading, setLoading] = useState(enabled)

  const refresh = useCallback(async () => {
    if (!enabled) return
    try {
      const res = await fetch(`/api/trips/${tripId}/cart`)
      const json = await res.json().catch(() => ({}))
      if (res.ok && json.cart) setWire(json.cart as PricedCartWire)
    } catch {
      // network hiccup — keep what we have
    } finally {
      setLoading(false)
    }
  }, [enabled, tripId])

  useEffect(() => {
    // A new trip / user means a different cart: start from empty and load it.
    let alive = true
    const load = async () => {
      if (!enabled) {
        setWire(EMPTY_CART)
        setLoading(false)
        return
      }
      try {
        const res = await fetch(`/api/trips/${tripId}/cart`)
        const json = await res.json().catch(() => ({}))
        if (!alive) return
        setWire(res.ok && json.cart ? (json.cart as PricedCartWire) : EMPTY_CART)
      } catch {
        if (alive) setWire(EMPTY_CART)
      } finally {
        if (alive) setLoading(false)
      }
    }
    void load()
    return () => {
      alive = false
    }
  }, [tripId, enabled])

  const items = useMemo(() => wire.items.map(toItem), [wire.items])

  const add = useCallback(
    async (item: { permitId: string; productCode: string; quantity?: number }): Promise<boolean> => {
      if (!enabled) return false
      if (wire.items.some((i) => i.permit_id === item.permitId)) return true
      const res = await fetch(`/api/trips/${tripId}/cart/items`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ permit_id: item.permitId, product_code: item.productCode, quantity: item.quantity ?? 1 }),
      })
      const json = await res.json().catch(() => ({}))
      if (!res.ok || !json.cart) {
        toast.error(json.error ?? 'Could not add the route to your cart')
        return false
      }
      setWire(json.cart as PricedCartWire)
      void refresh()
      return true
    },
    [enabled, tripId, wire.items, refresh],
  )

  const removeLine = useCallback(
    async (line: PricedCartLineWire) => {
      // Optimistic: drop the line now, trust the server's copy afterwards.
      setWire((w) => ({ ...w, items: w.items.filter((i) => i.id !== line.id) }))
      const res = await fetch(`/api/trips/${tripId}/cart/items/${line.id}`, { method: 'DELETE' })
      const json = await res.json().catch(() => ({}))
      if (!res.ok || !json.cart) {
        toast.error(json.error ?? 'Could not remove the route from your cart')
        await refresh()
        return
      }
      setWire(json.cart as PricedCartWire)
    },
    [tripId, refresh],
  )

  const remove = useCallback(
    async (permitId: string) => {
      const line = wire.items.find((i) => i.permit_id === permitId)
      if (line) await removeLine(line)
    },
    [wire.items, removeLine],
  )

  const clear = useCallback(async () => {
    for (const line of wire.items) await removeLine(line)
    await refresh()
  }, [wire.items, removeLine, refresh])

  return {
    items,
    loading,
    has: (permitId) => items.some((i) => i.permitId === permitId),
    add,
    remove,
    clear,
    refresh,
    totalCents: wire.total_cents,
    subtotalCents: wire.subtotal_cents,
    currency: wire.currency,
  }
}

/* ---------------- The button on a permit / state ---------------- */

async function orderRoute(
  tripId: string,
  stateCode: string,
  routeType: RouteType | undefined,
  paidWith: 'credit' | 'card',
): Promise<string | null> {
  const res = await fetch(`/api/trips/${tripId}/requests`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: 'route_request',
      state_code: stateCode,
      notes: '',
      paid_with: paidWith,
      ...(routeType ? { route_type: routeType } : {}),
    }),
  })
  const json = await res.json().catch(() => ({}))
  return res.ok ? null : (json.error ?? 'Could not order the route')
}

const EXPLAINER =
  "You're buying the route for this permit — a Google Maps route (in parts where the state needs it), a GPX file for the truck GPS, and a Hummer GPS link. Our trusted partner Synchron Permits reads the permit and builds the map with a pin for every exit, so it's easy to see which way the truck goes. One purchase unlocks it for everyone on this trip."

/**
 * Checkout hand-off shared by the cart dialog and the purchase history:
 * asks the server for the Synchron checkout URL and sends the browser there.
 * Returns an error message (already toasted) when it could not.
 */
export async function startCheckout(purchaseId: string): Promise<string | null> {
  const res = await fetch(`/api/purchases/${purchaseId}/checkout`, { method: 'POST' })
  const json = await res.json().catch(() => ({}))
  if (!res.ok || !json.checkout_url) {
    const message = json.error ?? 'Checkout is not available right now. Your selections are saved.'
    toast.error(message)
    return message
  }
  window.location.assign(json.checkout_url as string)
  return null
}

export function RoutePurchaseControl({
  tripId,
  permitId,
  stateCode,
  routeType,
  credits,
  cart,
  onOrdered,
  variant = 'workspace',
}: {
  tripId: string
  /** The permit the route is for — the cart is keyed by permit (D3). */
  permitId?: string
  stateCode: string
  /** Express / Extended is assigned by the backend; undefined = not known yet. */
  routeType?: RouteType
  credits: RouteCredits
  cart: RouteCart
  onOrdered: () => void
  variant?: 'workspace' | 'driver'
}) {
  const [confirming, setConfirming] = useState(false)
  const [pending, setPending] = useState(false)
  const [choosing, setChoosing] = useState(false)
  const [chosen, setChosen] = useState<string | null>(null)
  const { products, loading: productsLoading } = useRoutingProducts()
  // Express is the standard product; Extended is the exception Synchron
  // assigns when a state needs it. A route whose type is not known yet is
  // priced as Express until the backend says otherwise.
  const effectiveType: RouteType = routeType ?? 'express'
  // Prepaid credits cover Express Routes only (strategy doc; unchanged).
  const creditsApply = effectiveType === 'express' && credits.remaining > 0

  const btn =
    variant === 'driver'
      ? 'rounded-lg bg-navy-900 px-3 py-1.5 text-[11px] font-bold text-white hover:bg-navy-800 disabled:opacity-60'
      : 'rounded-md bg-[#f5a623] px-2 py-0.5 text-[11px] font-bold text-[#0f1b2d] transition hover:bg-[#d98b06] disabled:opacity-60'

  async function spendCredit() {
    setPending(true)
    try {
      const error = await orderRoute(tripId, stateCode, effectiveType, 'credit')
      if (error) {
        toast.error(error)
        return
      }
      const left = Math.max(credits.remaining - 1, 0)
      toast.success(
        `Route ordered for ${stateName(stateCode)} — 1 Express Route credit used, ${left} left. It unlocks for everyone on this trip.`,
      )
      setConfirming(false)
      onOrdered()
    } finally {
      setPending(false)
    }
  }

  if (creditsApply) {
    return (
      <span className="group/buy relative inline-block">
        <button
          type="button"
          onClick={() => setConfirming((c) => !c)}
          disabled={pending}
          className={btn}
          title={EXPLAINER}
        >
          Use 1 Express Route credit · {credits.remaining} left
        </button>
        {/* Light inline confirm — a mis-tap on a phone must never silently
            burn a prepaid credit; still two taps, no cart, balance visible at
            the moment of spending. */}
        {confirming && (
          <span
            role="dialog"
            className="absolute right-0 top-full z-30 mt-1.5 w-64 rounded-xl border border-line bg-white p-3 text-left text-[11px] font-normal leading-relaxed text-ink shadow-xl"
          >
            <span className="block font-bold">{stateName(stateCode)} Express Route</span>
            <span className="mt-0.5 block text-ok">
              ✓ Covered by your plan ({credits.remaining} of {credits.included} Express Routes left)
            </span>
            <span className="mt-2 flex gap-2">
              <button
                type="button"
                onClick={spendCredit}
                disabled={pending}
                className="rounded-lg bg-navy-900 px-3 py-1.5 font-bold text-white disabled:opacity-60"
              >
                {pending ? '…' : 'Use 1 credit'}
              </button>
              <button
                type="button"
                onClick={() => setConfirming(false)}
                disabled={pending}
                className="rounded-lg border border-line px-3 py-1.5 font-bold text-slate-body"
              >
                Cancel
              </button>
            </span>
          </span>
        )}
      </span>
    )
  }

  // The cart is keyed by permit; without a permit id there is nothing to add.
  if (!permitId) return null

  if (cart.has(permitId)) {
    return (
      <span
        className={`inline-block rounded-md px-2 py-1 text-[10px] font-bold text-ok ring-1 ring-inset ring-ok/30 ${
          variant === 'driver' ? 'bg-ok-bg' : 'bg-green-50'
        }`}
        title="In your cart — use Review cart to order it"
      >
        🛒 In cart
      </span>
    )
  }

  const defaultCode = productCodeForRouteType(effectiveType)
  const selectedCode = chosen ?? (products.some((p) => p.product_code === defaultCode) ? defaultCode : products[0]?.product_code ?? null)

  async function addToCart() {
    if (!permitId || !selectedCode) return
    setPending(true)
    try {
      const added = await cart.add({ permitId, productCode: selectedCode })
      if (!added) return
      const product = products.find((p) => p.product_code === selectedCode)
      toast.success(`${stateName(stateCode)} ${product?.product_name ?? 'route'} added to your cart`)
      setChoosing(false)
    } finally {
      setPending(false)
    }
  }

  return (
    <span className="group/buy relative inline-block">
      <button
        type="button"
        onClick={() => setChoosing((c) => !c)}
        disabled={pending}
        className={btn}
        title={`${EXPLAINER}${
          effectiveType === 'extended'
            ? ' This state requires Extended Route processing; included Express Route credits cannot be used for it.'
            : credits.included > 0
              ? ' You have used all included Express Route credits this month.'
              : ''
        }`}
      >
        Purchase route
      </button>
      {choosing && (
        <span
          role="dialog"
          className="absolute right-0 top-full z-30 mt-1.5 w-64 rounded-xl border border-line bg-white p-3 text-left text-[11px] font-normal leading-relaxed text-ink shadow-xl"
        >
          <span className="block font-bold">{stateName(stateCode)} route</span>
          {productsLoading ? (
            <span className="mt-1 block text-slate-body">Loading prices…</span>
          ) : products.length === 0 ? (
            <span className="mt-1 block text-slate-body">Route purchasing is not available right now.</span>
          ) : (
            <span role="radiogroup" className="mt-2 block space-y-1">
              {products.map((p) => {
                const active = p.product_code === selectedCode
                return (
                  <button
                    key={p.product_code}
                    type="button"
                    role="radio"
                    aria-checked={active}
                    onClick={() => setChosen(p.product_code)}
                    disabled={pending}
                    className={`flex w-full items-center justify-between rounded-lg border px-2.5 py-1.5 text-left font-semibold ${
                      active ? 'border-navy-900 bg-navy-900/5 text-ink' : 'border-line text-slate-body hover:bg-neutral-50'
                    }`}
                  >
                    <span>{p.product_name}</span>
                    <span className="font-mono">{formatCents(p.unit_price_cents, p.currency)}</span>
                  </button>
                )
              })}
            </span>
          )}
          <span className="mt-2 flex gap-2">
            <button
              type="button"
              onClick={addToCart}
              disabled={pending || !selectedCode}
              className="rounded-lg bg-navy-900 px-3 py-1.5 font-bold text-white disabled:opacity-60"
            >
              {pending ? '…' : 'Add to cart'}
            </button>
            <button
              type="button"
              onClick={() => setChoosing(false)}
              disabled={pending}
              className="rounded-lg border border-line px-3 py-1.5 font-bold text-slate-body"
            >
              Cancel
            </button>
          </span>
        </span>
      )}
    </span>
  )
}

/* ---------------- The active cart on the page ---------------- */

export function CartBar({
  tripId,
  cart,
  onPaid,
  variant = 'workspace',
}: {
  tripId: string
  cart: RouteCart
  /** Called after a purchase is created (before the redirect to checkout). */
  onPaid: () => void
  variant?: 'workspace' | 'driver'
}) {
  const [open, setOpen] = useState(false)
  const [paying, setPaying] = useState(false)
  if (cart.items.length === 0) return null

  async function completePurchase() {
    setPaying(true)
    try {
      const res = await fetch(`/api/trips/${tripId}/purchases`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({}),
      })
      const json = await res.json().catch(() => ({}))
      if (!res.ok || !json.purchase?.id) {
        toast.error(json.error ?? 'Could not start the purchase. Your selections are saved.')
        return
      }
      onPaid()
      const error = await startCheckout(json.purchase.id as string)
      if (error) return
      // Redirecting — keep the button disabled until the page unloads.
      return
    } catch {
      toast.error('Could not start the purchase. Your selections are saved.')
    } finally {
      setPaying(false)
    }
  }

  const n = cart.items.length
  return (
    <>
      <div
        className={
          variant === 'driver'
            ? 'flex items-center justify-between gap-2 rounded-2xl border border-amber-brand/60 bg-white p-3 text-xs shadow-sm'
            : 'flex items-center justify-between gap-2 border-b bg-amber-50 px-4 py-2.5 text-xs'
        }
      >
        <p className="min-w-0 truncate font-semibold text-ink">
          🛒 {n} route{n === 1 ? '' : 's'} in cart · {dollars(cart.totalCents)}
        </p>
        <span className="flex shrink-0 items-center gap-1.5">
          <Button size="sm" onClick={() => setOpen(true)} className="h-7 bg-[#0f1b2d] text-[11px] font-bold">
            Review cart
          </Button>
          <Button size="sm" variant="ghost" onClick={() => void cart.clear()} className="h-7 text-[11px]">
            Clear
          </Button>
        </span>
      </div>

      <Dialog open={open} onOpenChange={(o) => !paying && setOpen(o)}>
        <DialogContent className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>Your routes ({n})</DialogTitle>
          </DialogHeader>
          <ul className="divide-y text-sm">
            {cart.items.map((item) => (
              <li key={item.id} className="py-2">
                <div className="flex items-center justify-between gap-2">
                  <span className="min-w-0">
                    <span className="font-semibold">{item.stateCode ? `${stateName(item.stateCode)} Permit` : 'Permit'}</span>
                    <span className="ml-1.5 text-xs text-neutral-500">— {item.productName}</span>
                  </span>
                  <span className="flex shrink-0 items-center gap-2">
                    <span className="font-mono text-xs">{dollars(item.lineTotalCents)}</span>
                    <button
                      type="button"
                      onClick={() => void cart.remove(item.permitId)}
                      disabled={paying}
                      className="text-xs text-neutral-400 hover:text-red-600"
                      title="Remove from cart"
                      aria-label="Remove"
                    >
                      ✕
                    </button>
                  </span>
                </div>
                {item.warning && <p className="mt-0.5 text-[11px] text-amber-700">{item.warning}</p>}
              </li>
            ))}
          </ul>
          <p className="flex items-center justify-between border-t pt-3 text-sm font-bold">
            <span>Total</span>
            <span className="font-mono">{dollars(cart.totalCents)}</span>
          </p>
          <p className="rounded-lg bg-amber-50 p-2.5 text-[11px] leading-relaxed text-amber-900">
            Payment is collected securely by Synchron Permits. Routes unlock for everyone on this trip
            once payment is confirmed.
          </p>
          <div className="flex justify-end gap-2">
            <Button variant="ghost" onClick={() => setOpen(false)} disabled={paying}>
              Continue shopping
            </Button>
            <Button
              onClick={completePurchase}
              disabled={paying}
              className="bg-[#f5a623] font-bold text-[#0f1b2d] hover:bg-[#d98b06]"
            >
              {paying ? 'Starting checkout…' : 'Complete Purchase'}
            </Button>
          </div>
        </DialogContent>
      </Dialog>
    </>
  )
}
