import {
  CheckCircle2,
  ChevronLeft,
  ChevronRight,
  Clock3,
  ExternalLink,
  FileWarning,
  Lock,
  LockOpen,
  Sparkles,
} from "lucide-react";
import { useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";

import { Alert } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { CopyTicketButton } from "@/components/CopyTicketButton";
import { HelpLink } from "@/components/HelpLink";
import { acknowledgeMissingTicketDayBilling, closePeriod, getStatisticsOverview, reopenPeriod } from "../api";
import { currentMonth, formatDate, formatDateTime, formatMinutes } from "../format";
import type { StatisticsCrmDay, StatisticsOpenSession, StatisticsOverview } from "../types";

type MonthlyClosePageProps = {
  onNavigate: (to: string) => void;
};

const trackedTextClass = "text-sky-700 dark:text-sky-300";
const teamspaceTextClass = "text-emerald-700 dark:text-emerald-300";
const showAcknowledgedMissingCrmStorageKey = "tickettracker.monthlyClose.showAcknowledgedMissingCrm";

function formatSignedMinutes(minutes: number) {
  if (minutes === 0) {
    return "ausgeglichen";
  }

  return `${minutes > 0 ? "+" : "-"}${formatMinutes(Math.abs(minutes))}`;
}

function formatTeamspaceDelta(minutes: number) {
  return formatSignedMinutes(minutes);
}

function StatusCard({ label, value, detail, tone }: { label: string; value: string | number; detail: string; tone?: "ok" | "warn" | "neutral" }) {
  const valueClassName = tone === "ok" ? "text-emerald-700 dark:text-emerald-300" : tone === "warn" ? "text-amber-700 dark:text-amber-300" : "";

  return (
    <Card>
      <CardContent className="p-3">
        <p className="text-xs font-medium text-muted-foreground">{label}</p>
        <p className={`text-xl font-semibold tracking-normal ${valueClassName}`}>{value}</p>
        <p className="text-xs text-muted-foreground">{detail}</p>
      </CardContent>
    </Card>
  );
}

type OpenTicketGroup = {
  ticketId: string;
  ticketNumber: string;
  organizationName: string;
  openCount: number;
  totalMinutes: number;
};

function groupOpenSessions(sessions: StatisticsOpenSession[]) {
  const groups = new Map<string, OpenTicketGroup>();

  for (const session of sessions) {
    const existing = groups.get(session.ticket_id);

    if (existing) {
      existing.openCount += 1;
      existing.totalMinutes += session.rounded_minutes;
      continue;
    }

    groups.set(session.ticket_id, {
      ticketId: session.ticket_id,
      ticketNumber: session.ticket_number,
      organizationName: session.organization_name,
      openCount: 1,
      totalMinutes: session.rounded_minutes
    });
  }

  return Array.from(groups.values()).sort((left, right) => left.ticketNumber.localeCompare(right.ticketNumber, "de", { numeric: true, sensitivity: "base" }));
}

function OpenTicketItem({ group, month, onNavigate }: { group: OpenTicketGroup; month: string; onNavigate: (to: string) => void }) {
  return (
    <div className="flex min-h-36 flex-col justify-between gap-3 rounded-md border bg-background p-3">
      <div className="min-w-0">
        <div className="flex flex-wrap items-center gap-2">
          <span className="font-medium">{group.ticketNumber}</span>
          <CopyTicketButton ticketNumber={group.ticketNumber} />
          <Badge variant="warning">{group.openCount} offen</Badge>
        </div>
        <p className="truncate text-sm text-muted-foreground">{group.organizationName}</p>
        <p className={`text-sm font-medium ${trackedTextClass}`}>{formatMinutes(group.totalMinutes)} offen zu bewerten</p>
      </div>
      <div className="flex justify-end">
        <Button variant="secondary" size="sm" onClick={() => onNavigate(`/analysis/month/${month}/tickets/${group.ticketId}`)}>
          Ticket öffnen
          <ExternalLink className="size-4" />
        </Button>
      </div>
    </div>
  );
}

function CrmDayItem({
  item,
  busy,
  onSetAcknowledgement,
  onNavigate
}: {
  item: StatisticsCrmDay;
  busy: boolean;
  onSetAcknowledgement: (item: StatisticsCrmDay, acknowledged: boolean) => void;
  onNavigate: (to: string) => void;
}) {
  const isAcknowledged = Boolean(item.acknowledged);

  return (
    <div className={`flex min-h-36 flex-col justify-between gap-3 rounded-md border bg-background p-3 ${isAcknowledged ? "opacity-75" : ""}`}>
      <div className="min-w-0">
        <div className="flex flex-wrap items-center gap-2">
          <span className="font-medium">{item.ticket_number}</span>
          <CopyTicketButton ticketNumber={item.ticket_number} />
          <Badge variant="outline">{formatDate(`${item.day}T00:00:00`)}</Badge>
          <Badge variant={isAcknowledged ? "success" : "warning"}>{isAcknowledged ? "zur Kenntnis" : "offen"}</Badge>
        </div>
        <p className="truncate text-sm text-muted-foreground">{item.organization_name}</p>
        <p className="flex flex-wrap gap-x-3 gap-y-1 text-xs">
          <span className={trackedTextClass}>Abgerechnet {formatMinutes(item.tracked_minutes)}</span>
          <span className={teamspaceTextClass}>Teamspace {formatMinutes(item.crm_billed_minutes)}</span>
          {typeof item.delta_minutes === "number" ? <span className="text-muted-foreground">Differenz {formatTeamspaceDelta(item.delta_minutes)}</span> : null}
        </p>
      </div>
      <div className="flex flex-wrap justify-end gap-2">
        <Button variant={isAcknowledged ? "secondary" : "ghost"} size="sm" disabled={busy} onClick={() => onSetAcknowledgement(item, !isAcknowledged)}>
          {isAcknowledged ? "Zurücknehmen" : "Zur Kenntnis"}
        </Button>
        <Button variant="secondary" size="sm" onClick={() => onNavigate(`/analysis/day/${item.day}/tickets/${item.ticket_id}`)}>
          Tag öffnen
          <ExternalLink className="size-4" />
        </Button>
      </div>
    </div>
  );
}

export function MonthlyClosePage({ onNavigate }: MonthlyClosePageProps) {
  const [month, setMonth] = useState(currentMonth());
  const [stats, setStats] = useState<StatisticsOverview | null>(null);
  const [loading, setLoading] = useState(false);
  const [closing, setClosing] = useState(false);
  const [reopening, setReopening] = useState(false);
  const [acknowledgingKeys, setAcknowledgingKeys] = useState<Set<string>>(() => new Set());
  const [showAcknowledgedMissingCrm, setShowAcknowledgedMissingCrm] = useState(false);
  const [showAcknowledgedMissingCrmLoaded, setShowAcknowledgedMissingCrmLoaded] = useState(false);
  const loadRequestId = useRef(0);

  async function load() {
    const requestId = loadRequestId.current + 1;
    loadRequestId.current = requestId;
    setLoading(true);

    try {
      const result = await getStatisticsOverview(month);

      if (requestId === loadRequestId.current) {
        setStats(result);
      }
    } catch (error) {
      if (requestId === loadRequestId.current) {
        toast.error("Monatsabschluss konnte nicht geladen werden", {
          description: error instanceof Error ? error.message : "Unbekannter Fehler"
        });
      }
    } finally {
      if (requestId === loadRequestId.current) {
        setLoading(false);
      }
    }
  }

  useEffect(() => {
    void load();
  }, [month]);

  useEffect(() => {
    setShowAcknowledgedMissingCrm(window.localStorage.getItem(showAcknowledgedMissingCrmStorageKey) === "true");
    setShowAcknowledgedMissingCrmLoaded(true);
  }, []);

  useEffect(() => {
    if (showAcknowledgedMissingCrmLoaded) {
      window.localStorage.setItem(showAcknowledgedMissingCrmStorageKey, String(showAcknowledgedMissingCrm));
    }
  }, [showAcknowledgedMissingCrm, showAcknowledgedMissingCrmLoaded]);

  function changeMonthBy(delta: number) {
    const [year, monthNumber] = month.split("-").map(Number);
    const date = Number.isFinite(year) && Number.isFinite(monthNumber) ? new Date(year, monthNumber - 1, 1) : new Date();
    date.setMonth(date.getMonth() + delta);
    setMonth(`${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`);
  }

  async function finishMonth() {
    setClosing(true);
    try {
      await closePeriod("month", month);
      toast.success("Monat abgeschlossen", {
        description: `${month} ist abgeschlossen.`
      });
      await load();
    } catch (error) {
      toast.error("Monat noch nicht abschließbar", {
        description: error instanceof Error ? error.message : "Bitte offene Bewertungen prüfen."
      });
      await load();
    } finally {
      setClosing(false);
    }
  }

  async function reopenMonth() {
    setReopening(true);
    try {
      await reopenPeriod("month", month);
      toast.success("Monat wieder geöffnet", {
        description: `${month} kann wieder bearbeitet werden.`
      });
      await load();
    } catch (error) {
      toast.error("Monat konnte nicht geöffnet werden", {
        description: error instanceof Error ? error.message : "Unbekannter Fehler"
      });
    } finally {
      setReopening(false);
    }
  }

  async function setMissingCrmAcknowledgement(item: StatisticsCrmDay, acknowledged: boolean) {
    const key = `${item.ticket_id}:${item.day}`;
    setAcknowledgingKeys((current) => new Set(current).add(key));

    try {
      await acknowledgeMissingTicketDayBilling(item.ticket_id, item.day, acknowledged);
      toast.success(acknowledged ? "Teamspace-Prüfung abgehakt" : "Kenntnisnahme zurückgenommen", {
        description: `${item.ticket_number} am ${formatDate(`${item.day}T00:00:00`)}`
      });
      await load();
    } catch (error) {
      toast.error(acknowledged ? "Konnte nicht abgehakt werden" : "Konnte nicht zurückgenommen werden", {
        description: error instanceof Error ? error.message : "Unbekannter Fehler"
      });
    } finally {
      setAcknowledgingKeys((current) => {
        const next = new Set(current);
        next.delete(key);
        return next;
      });
    }
  }

  const openSessions = stats?.attention.openSessions ?? [];
  const missingCrmDays = stats?.attention.missingCrmDays ?? [];
  const acknowledgedMissingCrmDays = stats?.attention.acknowledgedMissingCrmDays ?? [];
  const openTicketGroups = useMemo(() => groupOpenSessions(openSessions), [openSessions]);
  const visibleMissingCrmDays = useMemo(() => {
    const days = showAcknowledgedMissingCrm ? [...missingCrmDays, ...acknowledgedMissingCrmDays] : [...missingCrmDays];
    return days.sort((left, right) => left.day.localeCompare(right.day) || left.ticket_number.localeCompare(right.ticket_number, "de", { numeric: true, sensitivity: "base" }));
  }, [acknowledgedMissingCrmDays, missingCrmDays, showAcknowledgedMissingCrm]);
  const canClose = Boolean(stats && !stats.closed && stats.totals.sessions > 0 && openSessions.length === 0);
  const checklist = useMemo(
    () => [
      {
        label: "Offene Bewertungen",
        ok: openSessions.length === 0,
        value: openSessions.length,
        detail: openSessions.length === 0 ? "Alle Sessions sind bewertet." : `${openTicketGroups.length} Ticket(s) bearbeiten`
      },
      {
        label: "Teamspace geprüft",
        ok: missingCrmDays.length === 0,
        value: missingCrmDays.length,
        detail: missingCrmDays.length === 0 ? "Keine offenen Teamspace-Prüfpunkte." : `${acknowledgedMissingCrmDays.length} zur Kenntnis genommen`
      },
      {
        label: "Monat abschließbar",
        ok: Boolean(stats?.closed || canClose),
        value: stats?.closed ? "geschlossen" : canClose ? "bereit" : "offen",
        detail: stats?.closed ? "Monat ist gesperrt." : canClose ? "Kann abgeschlossen werden." : "Bewertungen prüfen."
      }
    ],
    [acknowledgedMissingCrmDays.length, canClose, openSessions.length, openTicketGroups.length, missingCrmDays.length, stats?.closed]
  );

  return (
    <div className="space-y-5">
      <div className="flex flex-col gap-3 xl:flex-row xl:items-end xl:justify-between">
        <div>
          <h2 className="text-2xl font-semibold tracking-normal">Monatsabschluss</h2>
          <p className="text-sm text-muted-foreground">Offene Bewertungen, Teamspace-Abgleich und der eigentliche Monatsabschluss.</p>
        </div>
        <div className="grid gap-2 sm:grid-cols-[minmax(220px,300px)_auto] sm:items-end">
          <div className="space-y-1">
            <div className="flex items-center justify-between gap-2">
              <label className="text-xs font-medium text-muted-foreground" htmlFor="close-month">Monat</label>
              {loading ? <span className="text-xs text-muted-foreground">lädt...</span> : null}
            </div>
            <div className="grid grid-cols-[2.25rem_minmax(0,1fr)_2.25rem] gap-1">
              <Button type="button" size="icon" variant="secondary" className="h-9 w-9" onClick={() => changeMonthBy(-1)} aria-label="Monat zurück">
                <ChevronLeft className="size-4" />
              </Button>
              <Input id="close-month" className="h-9" type="month" value={month} onChange={(event) => setMonth(event.currentTarget.value)} />
              <Button type="button" size="icon" variant="secondary" className="h-9 w-9" onClick={() => changeMonthBy(1)} aria-label="Monat vor">
                <ChevronRight className="size-4" />
              </Button>
            </div>
          </div>
          {stats?.closed ? (
            <Button size="sm" variant="secondary" disabled={reopening} onClick={reopenMonth}>
              <LockOpen className="size-4" />
              {reopening ? "Öffnet..." : "Monat öffnen"}
            </Button>
          ) : (
            <Button size="sm" disabled={!canClose || closing} onClick={finishMonth}>
              <Lock className="size-4" />
              {closing ? "Schließt..." : "Monat abschließen"}
            </Button>
          )}
        </div>
      </div>

      {stats?.closed ? (
        <Alert variant="success" className="flex items-center gap-2 py-3">
          <CheckCircle2 className="size-4 shrink-0" />
          <span>Dieser Monat wurde am {formatDateTime(stats.closedAt!)} abgeschlossen.</span>
        </Alert>
      ) : null}

      <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-5">
        <StatusCard label="Sessions" value={stats?.totals.sessions ?? 0} detail={`${formatMinutes(stats?.totals.minutes ?? 0)} abgerechnet`} />
        <StatusCard label="Teamspace" value={formatMinutes(stats?.totals.crmBilledMinutes ?? 0)} detail={`Differenz ${formatTeamspaceDelta(stats?.totals.crmDeltaMinutes ?? 0)}`} />
        <StatusCard label="Offene Bewertungen" value={openSessions.length} detail={`${openTicketGroups.length} Ticket(s)`} tone={openSessions.length > 0 ? "warn" : "ok"} />
        <StatusCard label="Ohne Teamspace" value={missingCrmDays.length} detail={`${acknowledgedMissingCrmDays.length} zur Kenntnis genommen`} tone={missingCrmDays.length > 0 ? "warn" : "ok"} />
        <StatusCard label="Status" value={stats?.closed ? "geschlossen" : canClose ? "bereit" : "offen"} detail={stats?.closed ? "Monat ist gesperrt" : canClose ? "kann abgeschlossen werden" : "Prüfpunkte bearbeiten"} tone={stats?.closed || canClose ? "ok" : "warn"} />
      </div>

      <Card>
        <CardHeader className="p-4">
          <div className="flex items-start justify-between gap-3">
            <div>
              <CardTitle>Checkliste</CardTitle>
              <CardDescription>Bewertungen sind Pflicht. Teamspace-Punkte helfen beim sauberen CRM-Abgleich.</CardDescription>
            </div>
            <HelpLink anchor="monatsabschluss" label="Hilfe zum Monatsabschluss" />
          </div>
        </CardHeader>
        <CardContent className="grid gap-2 px-4 pb-4 md:grid-cols-3">
          {checklist.map((item) => (
            <div key={item.label} className="rounded-md border bg-background p-3">
              <div className="mb-2 flex items-center justify-between gap-2">
                <span className="text-sm font-medium">{item.label}</span>
                <Badge variant={item.ok ? "success" : "warning"}>{item.value}</Badge>
              </div>
              <p className="text-xs text-muted-foreground">{item.detail}</p>
            </div>
          ))}
        </CardContent>
      </Card>

      <Card>
        <CardHeader className="p-4">
          <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
            <div>
              <CardTitle>Aufräumen</CardTitle>
              <CardDescription>Alles, was für diesen Monat noch Aufmerksamkeit braucht.</CardDescription>
            </div>
            <div className="flex flex-wrap items-center gap-2">
              <div className="inline-flex h-8 items-center gap-2 rounded-md border bg-background px-3 text-sm">
                <Checkbox
                  checked={showAcknowledgedMissingCrm}
                  aria-label="Zur Kenntnis genommene Teamspace-Tage anzeigen"
                  onCheckedChange={(checked) => setShowAcknowledgedMissingCrm(checked === true)}
                />
                <span>Zur Kenntnis anzeigen ({acknowledgedMissingCrmDays.length})</span>
              </div>
              <HelpLink anchor="monatsabschluss" label="Hilfe zu offenen Bewertungen und Teamspace-Prüfpunkten" />
            </div>
          </div>
        </CardHeader>
        <CardContent className="grid gap-4 px-4 pb-4 xl:grid-cols-2">
          <section className="space-y-2">
            <div className="flex min-h-8 items-center gap-2 text-sm font-medium">
              <FileWarning className="size-4 text-muted-foreground" />
              Offene Bewertungen
            </div>
            {openTicketGroups.map((group) => (
              <OpenTicketItem key={group.ticketId} group={group} month={month} onNavigate={onNavigate} />
            ))}
            {openTicketGroups.length === 0 ? <p className="rounded-md border bg-muted/20 p-3 text-sm text-muted-foreground">Keine offenen Bewertungen.</p> : null}
          </section>

          <section className="space-y-2">
            <div className="flex min-h-8 items-center gap-2 text-sm font-medium">
              <Sparkles className="size-4 text-muted-foreground" />
              Tage ohne Teamspace-Wert
            </div>
            {visibleMissingCrmDays.map((item) => (
              <CrmDayItem
                key={`${item.ticket_id}-${item.day}-missing`}
                item={item}
                busy={acknowledgingKeys.has(`${item.ticket_id}:${item.day}`)}
                onSetAcknowledgement={setMissingCrmAcknowledgement}
                onNavigate={onNavigate}
              />
            ))}
            {visibleMissingCrmDays.length === 0 ? (
              <p className="rounded-md border bg-muted/20 p-3 text-sm text-muted-foreground">
                {acknowledgedMissingCrmDays.length > 0 ? "Alle offenen Punkte sind zur Kenntnis genommen." : "Alle abrechenbaren Tage haben einen Teamspace-Wert."}
              </p>
            ) : null}
          </section>
        </CardContent>
      </Card>

      {stats && stats.totals.sessions === 0 ? (
        <Card>
          <CardContent className="grid place-items-center gap-2 p-8 text-center">
            <Clock3 className="size-8 text-muted-foreground" />
            <div>
              <p className="font-medium">Keine Sessions in diesem Monat</p>
              <p className="text-sm text-muted-foreground">Ein Monatsabschluss ist erst sinnvoll, wenn Sessions vorhanden sind.</p>
            </div>
          </CardContent>
        </Card>
      ) : null}
    </div>
  );
}
