"use client"

import Link from "next/link"
import { useRouter } from "next/navigation"
import { useCallback, useEffect, useMemo, useState } from "react"
import { CircleAlert, ChevronLeft, ChevronRight, RefreshCw, Settings } from "lucide-react"

import { ListPageCard } from "@/app/dashboard/_components"
import { XmlApiErrorBanner } from "@/components/xml-apis/shared/xml-api-error-banner"
import { FRONTEND_ROUTES } from "@/config/frontend-routes"
import { dashboardCsrfHeader } from "@/lib/csrf.client"
import type {
  BankFetchFailureEvent,
  BankFetchFailureRow,
  IdLabelMap,
} from "@/components/xml-apis/bank-fetch-failures/types"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"

type Props = {
  initialRows: BankFetchFailureRow[]
  initialTotalCount: number
  initialPage: number
  initialPageSize: number
  initialPageCount: number
  bankApiOptions: IdLabelMap
  initialOpenCount: number
  initialErrorMessage?: string | null
}

type DayCounts = {
  fetch: number
  insert: number
  inserted: number
  reviewed: number
}

const COLORS = {
  fetch: "#c62828",
  insert: "#ef6c00",
  inserted: "#2e7d32",
  reviewed: "#9e9e9e",
} as const

function pad2(n: number): string {
  return String(n).padStart(2, "0")
}

function toYmd(d: Date): string {
  return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`
}

function monthRange(year: number, monthIndex: number): { start: string; end: string } {
  const start = new Date(year, monthIndex, 1)
  const end = new Date(year, monthIndex + 1, 1)
  return { start: toYmd(start), end: toYmd(end) }
}

function daysInMonthGrid(year: number, monthIndex: number): (string | null)[] {
  const first = new Date(year, monthIndex, 1)
  const startPad = (first.getDay() + 6) % 7
  const count = new Date(year, monthIndex + 1, 0).getDate()
  const cells: (string | null)[] = []
  for (let i = 0; i < startPad; i++) cells.push(null)
  for (let day = 1; day <= count; day++) {
    cells.push(`${year}-${pad2(monthIndex + 1)}-${pad2(day)}`)
  }
  while (cells.length % 7 !== 0) cells.push(null)
  return cells
}

function summarizeDay(dayEvents: BankFetchFailureEvent[]): DayCounts {
  const counts: DayCounts = { fetch: 0, insert: 0, inserted: 0, reviewed: 0 }
  for (const ev of dayEvents) {
    if (ev.is_success) {
      counts.inserted += 1
    } else if (!ev.open) {
      counts.reviewed += 1
    } else if (ev.failure_type === "insert") {
      counts.insert += 1
    } else {
      counts.fetch += 1
    }
  }
  return counts
}

function bankTitle(ev: BankFetchFailureEvent): string {
  return ev.title.replace(/^\[(Fetch|Insert|OK)\]\s*/i, "").trim() || ev.title
}

function typeLabel(ev: { failure_type: string; is_success?: boolean; open?: number | boolean }): string {
  if (ev.is_success) return "Inserted"
  if (ev.open === 0 || ev.open === false) return "Reviewed"
  return ev.failure_type === "insert" ? "Not inserted" : "Missing fetch"
}

function typeColor(ev: { failure_type: string; is_success?: boolean; open?: number | boolean }): string {
  if (ev.is_success) return COLORS.inserted
  if (ev.open === 0 || ev.open === false) return COLORS.reviewed
  return ev.failure_type === "insert" ? COLORS.insert : COLORS.fetch
}

function CountChip({ label, count, color }: { label: string; count: number; color: string }) {
  if (count <= 0) return null
  return (
    <span
      className="inline-flex items-center rounded px-1 py-0.5 text-[10px] font-semibold text-white"
      style={{ backgroundColor: color }}
      title={`${count} ${label}`}
    >
      {count}
    </span>
  )
}

export function BankFetchFailuresPage({
  initialRows,
  initialOpenCount,
  initialErrorMessage = null,
}: Props) {
  const router = useRouter()
  const now = new Date()
  const [year, setYear] = useState(now.getFullYear())
  const [monthIndex, setMonthIndex] = useState(now.getMonth())
  const [rows, setRows] = useState(initialRows)
  const [events, setEvents] = useState<BankFetchFailureEvent[]>([])
  const [openCount, setOpenCount] = useState(initialOpenCount)
  const [dayDetail, setDayDetail] = useState<string | null>(null)
  const [showInserted, setShowInserted] = useState(true)
  const [showReviewed, setShowReviewed] = useState(true)
  const [busy, setBusy] = useState(false)
  const [errorMessage, setErrorMessage] = useState(initialErrorMessage)
  const [statusMessage, setStatusMessage] = useState<string | null>(null)

  const monthLabel = useMemo(
    () => new Date(year, monthIndex, 1).toLocaleString(undefined, { month: "long", year: "numeric" }),
    [year, monthIndex]
  )

  const eventsByDate = useMemo(() => {
    const map = new Map<string, BankFetchFailureEvent[]>()
    for (const ev of events) {
      if (ev.is_success && !showInserted) continue
      if (!ev.is_success && !ev.open && !showReviewed) continue
      const key = ev.start.slice(0, 10)
      const list = map.get(key) || []
      list.push(ev)
      map.set(key, list)
    }
    return map
  }, [events, showInserted, showReviewed])

  const loadEvents = useCallback(async () => {
    const range = monthRange(year, monthIndex)
    try {
      const res = await fetch(FRONTEND_ROUTES.xmlApis.bankFetchFailures.events, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          ...dashboardCsrfHeader(),
        },
        body: JSON.stringify({
          start: range.start,
          end: range.end,
          include_success: showInserted ? "1" : "0",
          include_confirmed: showReviewed ? "1" : "0",
        }),
      })
      const json = await res.json().catch(() => ({}))
      if (!res.ok || json.status === "error") {
        setErrorMessage(json.message || "Could not load calendar events.")
        return
      }
      setEvents(Array.isArray(json?.data?.events) ? json.data.events : [])
      if (typeof json?.data?.open_count === "number") {
        setOpenCount(json.data.open_count)
      }
    } catch {
      setErrorMessage("Could not load calendar events.")
    }
  }, [year, monthIndex, showInserted, showReviewed])

  useEffect(() => {
    void loadEvents()
  }, [loadEvents])

  async function onScan() {
    setBusy(true)
    setErrorMessage(null)
    setStatusMessage(null)
    try {
      const res = await fetch(FRONTEND_ROUTES.xmlApis.bankFetchFailures.scan, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          ...dashboardCsrfHeader(),
        },
        body: JSON.stringify({ lookbackDays: 14 }),
      })
      const json = await res.json().catch(() => ({}))
      if (!res.ok || json.status === "error") {
        setErrorMessage(json.message || "Scan failed.")
        return
      }
      setStatusMessage(json.message || "Checked the last 14 days for missing fetches and pending inserts.")
      if (typeof json?.data?.open_count === "number") {
        setOpenCount(json.data.open_count)
      }
      await loadEvents()
      router.refresh()
    } finally {
      setBusy(false)
    }
  }

  async function onConfirm(id: number) {
    if (!id) return
    setBusy(true)
    setErrorMessage(null)
    try {
      const res = await fetch(FRONTEND_ROUTES.xmlApis.bankFetchFailures.confirm, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          ...dashboardCsrfHeader(),
        },
        body: JSON.stringify({ id }),
      })
      const json = await res.json().catch(() => ({}))
      if (!res.ok || json.status === "error") {
        setErrorMessage(json.message || "Could not mark as reviewed.")
        return
      }
      if (typeof json?.data?.open_count === "number") {
        setOpenCount(json.data.open_count)
      }
      setRows((prev) => prev.filter((r) => r.id !== id))
      setEvents((prev) =>
        showReviewed
          ? prev.map((ev) =>
              Number(ev.id) === id
                ? {
                    ...ev,
                    open: 0,
                    color: COLORS.reviewed,
                    confirmed_at: new Date().toISOString().slice(0, 19).replace("T", " "),
                  }
                : ev
            )
          : prev.filter((ev) => Number(ev.id) !== id)
      )
      await loadEvents()
      router.refresh()
    } finally {
      setBusy(false)
    }
  }

  function shiftMonth(delta: number) {
    const d = new Date(year, monthIndex + delta, 1)
    setYear(d.getFullYear())
    setMonthIndex(d.getMonth())
    setDayDetail(null)
  }

  const cells = daysInMonthGrid(year, monthIndex)
  const dayDetailEvents = dayDetail ? eventsByDate.get(dayDetail) || [] : []
  const dayCounts = summarizeDay(dayDetailEvents)

  type ListItem = {
    id: number | string
    failure_date: string
    failure_type: string
    bank_label: string
    message: string
    is_success: boolean
    open: boolean
    canReview: boolean
  }

  const listForPanel: ListItem[] = dayDetail
    ? dayDetailEvents.map((ev) => ({
        id: ev.id,
        failure_date: dayDetail,
        failure_type: ev.failure_type,
        bank_label: bankTitle(ev),
        message: ev.message,
        is_success: Boolean(ev.is_success),
        open: Boolean(ev.open),
        canReview: Boolean(ev.open) && !ev.is_success,
      }))
    : rows.map((row) => ({
        id: row.id,
        failure_date: row.failure_date,
        failure_type: row.failure_type,
        bank_label: row.bank_label,
        message: row.message,
        is_success: false,
        open: true,
        canReview: true,
      }))

  return (
    <div
      data-page-layout="full"
      className="flex h-full min-h-0 flex-1 flex-col overflow-hidden"
    >
      <ListPageCard
        icon={CircleAlert}
        title="Bank fetch & insert failures"
        description="Tracks banks that missed a daily XML fetch, or fetched XML that still has not been inserted. Mark a row reviewed after you handle it."
        breadcrumb={[{ label: "XML API" }, { label: "Bank fetch & insert failures" }]}
        shellClassName="h-full min-h-0 flex-1 gap-3 overflow-hidden md:gap-3"
        className="min-h-0 flex-1 overflow-hidden"
        contentClassName="flex min-h-0 flex-1 flex-col overflow-hidden"
        actions={
          <div className="flex items-center gap-2">
            <span className="rounded-md bg-red-600 px-2 py-1 text-xs font-medium text-white">
              {openCount} open
            </span>
            <Button
              type="button"
              size="sm"
              variant="outline"
              disabled={busy}
              title="Re-check the last 14 days for missing fetches and pending inserts"
              onClick={() => void onScan()}
            >
              <RefreshCw className="mr-1 h-3.5 w-3.5" />
              Rescan last 14 days
            </Button>
            <Link
              href="/dashboard/xml-apis/bank-fetch-failures/settings"
              className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground"
              title="Banks to monitor and Rocket.Chat alerts"
            >
              <Settings className="h-3.5 w-3.5" />
              Settings
            </Link>
          </div>
        }
      >
        <div className="shrink-0">
          <XmlApiErrorBanner message={errorMessage} />
          {statusMessage ? (
            <p className="mb-3 text-sm text-emerald-700">{statusMessage}</p>
          ) : null}
        </div>

        <div className="grid min-h-0 flex-1 gap-4 overflow-y-auto xl:grid-cols-2 xl:overflow-hidden">
          <section className="flex min-h-0 min-w-0 flex-col xl:overflow-hidden">
            <div className="mb-3 flex shrink-0 flex-wrap items-center justify-between gap-2">
              <div className="flex items-center gap-1">
                <Button type="button" variant="ghost" size="icon" onClick={() => shiftMonth(-1)}>
                  <ChevronLeft className="h-4 w-4" />
                </Button>
                <h2 className="min-w-[9rem] text-center text-sm font-semibold">{monthLabel}</h2>
                <Button type="button" variant="ghost" size="icon" onClick={() => shiftMonth(1)}>
                  <ChevronRight className="h-4 w-4" />
                </Button>
              </div>

              <div className="flex flex-wrap items-center gap-3 text-xs text-muted-foreground">
                <span className="inline-flex items-center gap-1.5">
                  <span className="h-2.5 w-2.5 rounded-sm" style={{ background: COLORS.fetch }} />
                  Missing fetch
                </span>
                <span className="inline-flex items-center gap-1.5">
                  <span className="h-2.5 w-2.5 rounded-sm" style={{ background: COLORS.insert }} />
                  Not inserted
                </span>
                <span className="inline-flex items-center gap-1.5">
                  <span className="h-2.5 w-2.5 rounded-sm" style={{ background: COLORS.inserted }} />
                  Inserted
                </span>
                <span className="inline-flex items-center gap-1.5">
                  <span className="h-2.5 w-2.5 rounded-sm" style={{ background: COLORS.reviewed }} />
                  Reviewed
                </span>
                <label className="inline-flex items-center gap-1.5">
                  <input
                    type="checkbox"
                    checked={showInserted}
                    onChange={(e) => setShowInserted(e.target.checked)}
                  />
                  Show inserted
                </label>
                <label className="inline-flex items-center gap-1.5">
                  <input
                    type="checkbox"
                    checked={showReviewed}
                    onChange={(e) => setShowReviewed(e.target.checked)}
                  />
                  Show reviewed
                </label>
              </div>
            </div>

            <div className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border">
              <div className="grid shrink-0 grid-cols-7 border-b bg-muted/30 text-center text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
                {["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map((d) => (
                  <div key={d} className="px-0.5 py-1.5">
                    {d}
                  </div>
                ))}
              </div>
              <div className="grid min-h-0 flex-1 grid-cols-7 auto-rows-fr">
                {cells.map((date, idx) => {
                  if (!date) {
                    return <div key={`pad-${idx}`} className="min-h-[2.75rem] border-b border-r bg-muted/10" />
                  }
                  const dayEvents = eventsByDate.get(date) || []
                  const counts = summarizeDay(dayEvents)
                  const hasIssues = counts.fetch + counts.insert > 0
                  const hasAnything =
                    hasIssues || counts.inserted > 0 || counts.reviewed > 0
                  const selected = dayDetail === date
                  return (
                    <button
                      key={date}
                      type="button"
                      onClick={() => setDayDetail((prev) => (prev === date ? null : date))}
                      className={cn(
                        "min-h-[2.75rem] border-b border-r p-1.5 text-left transition-colors hover:bg-muted/40",
                        selected && "bg-muted/50 ring-1 ring-inset ring-foreground/20",
                        !hasAnything && "text-muted-foreground"
                      )}
                    >
                      <div className="mb-0.5 text-[11px] font-medium">{Number(date.slice(-2))}</div>
                      {hasAnything ? (
                        <div className="flex flex-wrap gap-0.5">
                          <CountChip label="missing fetch" count={counts.fetch} color={COLORS.fetch} />
                          <CountChip label="not inserted" count={counts.insert} color={COLORS.insert} />
                          <CountChip label="inserted" count={counts.inserted} color={COLORS.inserted} />
                          <CountChip label="reviewed" count={counts.reviewed} color={COLORS.reviewed} />
                        </div>
                      ) : null}
                    </button>
                  )
                })}
              </div>
            </div>
          </section>

          <section className="flex min-h-0 min-w-0 flex-col xl:overflow-hidden">
            <div className="mb-2 flex shrink-0 flex-wrap items-center justify-between gap-2">
              <h3 className="text-sm font-semibold">
                {dayDetail
                  ? `Day ${dayDetail}`
                  : `Open failures${rows.length ? ` (${rows.length})` : ""}`}
              </h3>
              {dayDetail ? (
                <div className="flex items-center gap-3 text-xs text-muted-foreground">
                  {dayCounts.fetch > 0 ? <span>{dayCounts.fetch} missing</span> : null}
                  {dayCounts.insert > 0 ? <span>{dayCounts.insert} not inserted</span> : null}
                  {dayCounts.inserted > 0 ? <span>{dayCounts.inserted} inserted</span> : null}
                  {dayCounts.reviewed > 0 ? <span>{dayCounts.reviewed} reviewed</span> : null}
                  <button
                    type="button"
                    className="hover:text-foreground"
                    onClick={() => setDayDetail(null)}
                  >
                    Show all open
                  </button>
                </div>
              ) : null}
            </div>

            {listForPanel.length === 0 ? (
              <p className="flex min-h-0 flex-1 items-center justify-center rounded-lg border border-dashed px-3 py-8 text-center text-sm text-muted-foreground">
                {dayDetail ? "Nothing to show for this day." : "No open failures."}
              </p>
            ) : (
              <ul className="min-h-0 flex-1 divide-y overflow-y-auto rounded-lg border">
                {listForPanel.map((item) => (
                  <li key={String(item.id)} className="flex items-start gap-2.5 px-3 py-2.5">
                    <span
                      className="mt-0.5 w-[5.5rem] shrink-0 rounded px-1.5 py-0.5 text-center text-[10px] font-semibold text-white"
                      style={{
                        backgroundColor: typeColor({
                          failure_type: item.failure_type,
                          is_success: item.is_success,
                          open: item.open,
                        }),
                      }}
                    >
                      {typeLabel({
                        failure_type: item.failure_type,
                        is_success: item.is_success,
                        open: item.open,
                      })}
                    </span>
                    <div className="min-w-0 flex-1">
                      <div className="truncate text-sm font-medium">
                        {!dayDetail ? (
                          <button
                            type="button"
                            className="mr-2 text-muted-foreground hover:underline"
                            onClick={() => setDayDetail(item.failure_date)}
                          >
                            {item.failure_date}
                          </button>
                        ) : null}
                        {item.bank_label}
                      </div>
                      {item.message ? (
                        <div className="truncate text-xs text-muted-foreground" title={item.message}>
                          {item.message}
                        </div>
                      ) : null}
                    </div>
                    {item.canReview ? (
                      <Button
                        type="button"
                        size="sm"
                        variant="outline"
                        className="shrink-0"
                        disabled={busy}
                        title="Remove from open list after you have handled this bank/day"
                        onClick={() => void onConfirm(Number(item.id))}
                      >
                        Mark reviewed
                      </Button>
                    ) : null}
                  </li>
                ))}
              </ul>
            )}
          </section>
        </div>
      </ListPageCard>
    </div>
  )
}
