import { ChevronLeft, ChevronRight, Clock3, ExternalLink, RotateCcw, SlidersHorizontal, Ticket, UserCheck, Users } from "lucide-react";
import type { LucideIcon } from "lucide-react";
import { Fragment, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { CopyTicketButton } from "@/components/CopyTicketButton";
import { HelpLink } from "@/components/HelpLink";
import { getPeriodOverview } from "../api";
import { currentDay, currentMonth, formatMinutes } from "../format";
import type { PeriodOverview, PeriodType, TicketSummary } from "../types";

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

type TimelineBucket = {
  key: string;
  label: string;
  totalMinutes: number;
  sessionCount: number;
};

type TicketGroupBy = "none" | "workType" | "organization" | "status";
type TicketSortBy = "ticket" | "workType" | "organization" | "status";
type TicketSortDirection = "asc" | "desc";
type TicketStatusFilter = "all" | "open" | "evaluated";
type TicketWorkTypeFilter = "all" | "support" | "consulting";

type AnalysisTicketViewSettings = {
  groupBy: TicketGroupBy;
  sortBy: TicketSortBy;
  sortDirection: TicketSortDirection;
  query: string;
  workType: TicketWorkTypeFilter;
  organization: string;
  status: TicketStatusFilter;
};

type TicketSection = {
  key: string;
  label: string;
  tickets: TicketSummary[];
  ticketCount: number;
  totalMinutes: number;
  openCount: number;
};

const analysisTicketViewSettingsKey = "tickettracker.analysis.ticketViewSettings";

const defaultTicketViewSettings: AnalysisTicketViewSettings = {
  groupBy: "none",
  sortBy: "ticket",
  sortDirection: "asc",
  query: "",
  workType: "all",
  organization: "all",
  status: "all"
};

const groupByOptions: Array<{ value: TicketGroupBy; label: string }> = [
  { value: "none", label: "Keine" },
  { value: "workType", label: "Typ" },
  { value: "organization", label: "Organisation" },
  { value: "status", label: "Status" }
];

const sortByOptions: Array<{ value: TicketSortBy; label: string }> = [
  { value: "ticket", label: "Ticket" },
  { value: "workType", label: "Typ" },
  { value: "organization", label: "Organisation" },
  { value: "status", label: "Status" }
];

function readStoredTicketViewSettings(): AnalysisTicketViewSettings {
  if (typeof window === "undefined") {
    return defaultTicketViewSettings;
  }

  try {
    const raw = window.localStorage.getItem(analysisTicketViewSettingsKey);

    if (!raw) {
      return defaultTicketViewSettings;
    }

    const parsed = JSON.parse(raw) as Partial<AnalysisTicketViewSettings>;

    return {
      groupBy: groupByOptions.find((option) => option.value === parsed.groupBy)?.value ?? defaultTicketViewSettings.groupBy,
      sortBy: sortByOptions.find((option) => option.value === parsed.sortBy)?.value ?? defaultTicketViewSettings.sortBy,
      sortDirection: parsed.sortDirection === "desc" ? "desc" : defaultTicketViewSettings.sortDirection,
      query: typeof parsed.query === "string" ? parsed.query : defaultTicketViewSettings.query,
      workType: parsed.workType === "support" || parsed.workType === "consulting" ? parsed.workType : defaultTicketViewSettings.workType,
      organization: typeof parsed.organization === "string" ? parsed.organization : defaultTicketViewSettings.organization,
      status: parsed.status === "open" || parsed.status === "evaluated" ? parsed.status : defaultTicketViewSettings.status
    };
  } catch {
    return defaultTicketViewSettings;
  }
}

function TicketStatus({ ticket }: { ticket: TicketSummary }) {
  const ticketOpenCount = ticket.ticket_open_count ?? ticket.open_count;

  if (ticketOpenCount > 0) {
    return <Badge variant="warning">{ticketOpenCount} offen</Badge>;
  }

  return <Badge variant="success">bewertet</Badge>;
}

function ticketStatusKey(ticket: TicketSummary): "open" | "evaluated" {
  return (ticket.ticket_open_count ?? ticket.open_count) > 0 ? "open" : "evaluated";
}

function ticketStatusLabel(ticket: TicketSummary) {
  return ticketStatusKey(ticket) === "open" ? "Offen" : "Bewertet";
}

function workTypeLabel(workType: TicketSummary["work_type"]) {
  if (workType === "support") {
    return "Support";
  }

  if (workType === "consulting") {
    return "Consulting";
  }

  return "Ohne Typ";
}

function organizationLabel(ticket: TicketSummary) {
  return ticket.customer_name ?? ticket.organization_name ?? "Keine Organisation";
}

function organizationKey(ticket: TicketSummary) {
  return ticket.organization_id ? `id:${ticket.organization_id}` : `name:${organizationLabel(ticket)}`;
}

function compareText(left: string, right: string) {
  return left.localeCompare(right, "de", { numeric: true, sensitivity: "base" });
}

function sortTickets(tickets: TicketSummary[], sortBy: TicketSortBy, direction: TicketSortDirection) {
  const sorted = [...tickets].sort((left, right) => {
    let result = 0;

    if (sortBy === "ticket") {
      result = compareText(left.ticket_number, right.ticket_number);
    } else if (sortBy === "workType") {
      result = compareText(workTypeLabel(left.work_type), workTypeLabel(right.work_type));
    } else if (sortBy === "organization") {
      result = compareText(organizationLabel(left), organizationLabel(right));
    } else {
      result = compareText(ticketStatusLabel(left), ticketStatusLabel(right));
    }

    if (result === 0) {
      result = compareText(left.ticket_number, right.ticket_number);
    }

    return direction === "asc" ? result : -result;
  });

  return sorted;
}

function groupTicketSections(tickets: TicketSummary[], groupBy: TicketGroupBy): TicketSection[] {
  if (groupBy === "none") {
    return [
      {
        key: "all",
        label: "Alle Tickets",
        tickets,
        ticketCount: tickets.length,
        totalMinutes: tickets.reduce((sum, ticket) => sum + ticket.total_minutes, 0),
        openCount: tickets.reduce((sum, ticket) => sum + (ticket.ticket_open_count ?? ticket.open_count), 0)
      }
    ];
  }

  const groups = new Map<string, TicketSection>();

  for (const ticket of tickets) {
    const key =
      groupBy === "workType"
        ? `workType:${ticket.work_type ?? "none"}`
        : groupBy === "organization"
          ? organizationKey(ticket)
          : `status:${ticketStatusKey(ticket)}`;
    const label = groupBy === "workType" ? workTypeLabel(ticket.work_type) : groupBy === "organization" ? organizationLabel(ticket) : ticketStatusLabel(ticket);
    const existing = groups.get(key);

    if (existing) {
      existing.tickets.push(ticket);
      existing.ticketCount += 1;
      existing.totalMinutes += ticket.total_minutes;
      existing.openCount += ticket.ticket_open_count ?? ticket.open_count;
      continue;
    }

    groups.set(key, {
      key,
      label,
      tickets: [ticket],
      ticketCount: 1,
      totalMinutes: ticket.total_minutes,
      openCount: ticket.ticket_open_count ?? ticket.open_count
    });
  }

  return Array.from(groups.values()).sort((left, right) => {
    if (groupBy === "status") {
      return left.key === "status:open" ? -1 : right.key === "status:open" ? 1 : compareText(left.label, right.label);
    }

    return compareText(left.label, right.label);
  });
}

function buildChartPath(points: Array<{ x: number; y: number }>) {
  if (points.length === 0) {
    return "";
  }

  if (points.length === 1) {
    return `M ${points[0].x - 18} ${points[0].y} L ${points[0].x + 18} ${points[0].y}`;
  }

  return points.map((point, index) => `${index === 0 ? "M" : "L"} ${point.x} ${point.y}`).join(" ");
}

function niceCeilMinutes(value: number) {
  if (value <= 30) {
    return Math.max(5, Math.ceil(value / 5) * 5);
  }

  if (value <= 120) {
    return Math.ceil(value / 15) * 15;
  }

  if (value <= 480) {
    return Math.ceil(value / 30) * 30;
  }

  return Math.ceil(value / 60) * 60;
}

function formatAxisMinutes(value: number) {
  const minutes = Math.round(value);

  if (minutes === 0) {
    return "0";
  }

  if (minutes >= 60) {
    return `${new Intl.NumberFormat("de-DE", { maximumFractionDigits: minutes < 600 ? 1 : 0 }).format(minutes / 60)} h`;
  }

  return `${minutes} min`;
}

function buildTimeline(periodType: PeriodType, period: string, overview: PeriodOverview | null): TimelineBucket[] {
  const series = new Map((overview?.activitySeries ?? []).map((bucket) => [bucket.bucket_key, bucket]));

  if (periodType === "day") {
    return Array.from({ length: 24 }).map((_, hour) => {
      const key = String(hour).padStart(2, "0");
      const bucket = series.get(key);

      return {
        key,
        label: `${key}:00`,
        totalMinutes: bucket?.total_minutes ?? 0,
        sessionCount: bucket?.session_count ?? 0
      };
    });
  }

  const [year, month] = period.split("-").map(Number);
  const daysInMonth = Number.isFinite(year) && Number.isFinite(month) ? new Date(year, month, 0).getDate() : 31;

  return Array.from({ length: daysInMonth }).map((_, index) => {
    const day = String(index + 1).padStart(2, "0");
    const key = `${period}-${day}`;
    const bucket = series.get(key);

    return {
      key,
      label: day,
      totalMinutes: bucket?.total_minutes ?? 0,
      sessionCount: bucket?.session_count ?? 0
    };
  });
}

export function AnalysisPage({ onNavigate }: AnalysisPageProps) {
  const [periodType, setPeriodType] = useState<PeriodType>("month");
  const [period, setPeriod] = useState(currentMonth());
  const [overview, setOverview] = useState<PeriodOverview | null>(null);
  const [loading, setLoading] = useState(false);
  const [ticketViewSettings, setTicketViewSettings] = useState<AnalysisTicketViewSettings>(() => readStoredTicketViewSettings());
  const loadRequestId = useRef(0);

  useEffect(() => {
    window.localStorage.setItem(analysisTicketViewSettingsKey, JSON.stringify(ticketViewSettings));
  }, [ticketViewSettings]);

  async function load() {
    const requestId = loadRequestId.current + 1;
    loadRequestId.current = requestId;
    setLoading(true);
    try {
      const nextOverview = await getPeriodOverview(periodType, period);

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

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

  useEffect(() => {
    function refreshVisible() {
      if (document.visibilityState === "visible") {
        void load();
      }
    }

    const interval = window.setInterval(refreshVisible, 60_000);
    window.addEventListener("focus", refreshVisible);
    document.addEventListener("visibilitychange", refreshVisible);

    return () => {
      window.clearInterval(interval);
      window.removeEventListener("focus", refreshVisible);
      document.removeEventListener("visibilitychange", refreshVisible);
    };
  }, [periodType, period]);

  function switchPeriodType(nextType: PeriodType) {
    setPeriodType(nextType);
    setPeriod(nextType === "month" ? currentMonth() : currentDay());
  }

  function changePeriodBy(delta: number) {
    if (periodType === "month") {
      const [year, month] = period.split("-").map(Number);
      const date = Number.isFinite(year) && Number.isFinite(month) ? new Date(year, month - 1, 1) : new Date();
      date.setMonth(date.getMonth() + delta);
      setPeriod(`${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`);
    } else {
      const [year, month, day] = period.split("-").map(Number);
      const date =
        Number.isFinite(year) && Number.isFinite(month) && Number.isFinite(day)
          ? new Date(year, month - 1, day)
          : new Date();
      date.setDate(date.getDate() + delta);
      setPeriod(`${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`);
    }

  }

  const totals = overview?.totals;
  const periodLabel = periodType === "month" ? "Monat" : "Tag";
  const metricCards: Array<{ label: string; value: string | number; detail?: string; icon: LucideIcon }> = [
    { label: "Tickets", value: totals?.tickets ?? 0, icon: Ticket },
    { label: "Sessions", value: totals?.sessions ?? 0, icon: Users },
    { label: "Abgerechnet", value: formatMinutes(totals?.minutes ?? 0), detail: `Teamspace ${formatMinutes(totals?.crmBilledMinutes ?? 0)}`, icon: Clock3 },
    { label: "Nicht abrechenbar", value: formatMinutes(totals?.nonBillableMinutes ?? 0), icon: UserCheck },
    { label: "Offen", value: totals?.openSessions ?? 0, detail: formatMinutes(totals?.openMinutes ?? 0), icon: UserCheck }
  ];
  const tickets = overview?.tickets ?? [];
  const organizationOptions = useMemo(() => {
    const options = new Map<string, string>();

    for (const ticket of tickets) {
      options.set(organizationKey(ticket), organizationLabel(ticket));
    }

    return Array.from(options.entries())
      .map(([value, label]) => ({ value, label }))
      .sort((left, right) => compareText(left.label, right.label));
  }, [tickets]);
  const visibleTickets = useMemo(() => {
    const normalizedQuery = ticketViewSettings.query.trim().toLocaleLowerCase("de");
    const filtered = tickets.filter((ticket) => {
      const matchesQuery =
        !normalizedQuery ||
        ticket.ticket_number.toLocaleLowerCase("de").includes(normalizedQuery) ||
        organizationLabel(ticket).toLocaleLowerCase("de").includes(normalizedQuery);
      const matchesWorkType = ticketViewSettings.workType === "all" || ticket.work_type === ticketViewSettings.workType;
      const matchesOrganization = ticketViewSettings.organization === "all" || organizationKey(ticket) === ticketViewSettings.organization;
      const matchesStatus = ticketViewSettings.status === "all" || ticketStatusKey(ticket) === ticketViewSettings.status;

      return matchesQuery && matchesWorkType && matchesOrganization && matchesStatus;
    });

    return sortTickets(filtered, ticketViewSettings.sortBy, ticketViewSettings.sortDirection);
  }, [tickets, ticketViewSettings]);
  const ticketSections = useMemo(() => groupTicketSections(visibleTickets, ticketViewSettings.groupBy), [visibleTickets, ticketViewSettings.groupBy]);
  const activeFilterCount = [
    ticketViewSettings.query.trim(),
    ticketViewSettings.workType !== "all",
    ticketViewSettings.organization !== "all",
    ticketViewSettings.status !== "all",
    ticketViewSettings.groupBy !== "none",
    ticketViewSettings.sortBy !== "ticket",
    ticketViewSettings.sortDirection !== "asc"
  ].filter(Boolean).length;
  const timeline = buildTimeline(periodType, period, overview);
  const chartWidth = 900;
  const chartHeight = 180;
  const chartPadding = { top: 18, right: 18, bottom: 34, left: 64 };
  const chartInnerWidth = chartWidth - chartPadding.left - chartPadding.right;
  const chartInnerHeight = chartHeight - chartPadding.top - chartPadding.bottom;
  const maxMinutes = Math.max(...timeline.map((bucket) => bucket.totalMinutes), 1);
  const chartMaxMinutes = niceCeilMinutes(maxMinutes);
  const yAxisTicks = Array.from({ length: 5 }).map((_, index) => chartMaxMinutes - (index * chartMaxMinutes) / 4);
  const barWidth = Math.max(4, Math.min(18, chartInnerWidth / Math.max(timeline.length, 1) / 1.8));
  const chartPoints = timeline.map((bucket, index) => {
    const x =
      timeline.length === 1
        ? chartPadding.left + chartInnerWidth / 2
        : chartPadding.left + (index * chartInnerWidth) / (timeline.length - 1);

    return {
      bucket,
      x,
      barHeight: bucket.totalMinutes > 0 ? Math.max(2, (bucket.totalMinutes / chartMaxMinutes) * chartInnerHeight) : 0,
      y: chartPadding.top + (1 - bucket.totalMinutes / chartMaxMinutes) * chartInnerHeight
    };
  });
  const effortPath = buildChartPath(chartPoints.filter((point) => point.bucket.totalMinutes > 0).map((point) => ({ x: point.x, y: point.y })));
  const labelStep = periodType === "month" ? Math.max(1, Math.ceil(timeline.length / 8)) : 3;

  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">Auswertung</h2>
          <p className="text-sm text-muted-foreground">
            {periodType === "month" ? "Tickets prüfen, Sessions bewerten und Zeiten kontrollieren." : "Sessions des Tages prüfen und bewerten."}
          </p>
        </div>
        <div className="grid gap-2 sm:grid-cols-[auto_minmax(220px,300px)] sm:items-end">
          <div className="grid grid-cols-2 rounded-md border bg-background p-1">
            <Button size="sm" variant={periodType === "month" ? "default" : "ghost"} className="h-8" onClick={() => switchPeriodType("month")}>
              Monat
            </Button>
            <Button size="sm" variant={periodType === "day" ? "default" : "ghost"} className="h-8" onClick={() => switchPeriodType("day")}>
              Tag
            </Button>
          </div>
          <div className="space-y-1">
            <div className="flex items-center justify-between gap-2">
              <label className="text-xs font-medium text-muted-foreground" htmlFor="period">
                {periodLabel}
              </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={() => changePeriodBy(-1)} aria-label={`${periodLabel} zurück`}>
                <ChevronLeft className="size-4" />
              </Button>
              <Input
                id="period"
                className="h-9"
                type={periodType === "month" ? "month" : "date"}
                value={period}
                onChange={(event) => setPeriod(event.currentTarget.value)}
              />
              <Button type="button" size="icon" variant="secondary" className="h-9 w-9" onClick={() => changePeriodBy(1)} aria-label={`${periodLabel} vor`}>
                <ChevronRight className="size-4" />
              </Button>
            </div>
          </div>
        </div>
      </div>

      <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-5">
        {metricCards.map(({ label, value, detail, icon: Icon }) => (
          <Card key={label}>
            <CardContent className="flex items-center gap-3 p-3">
              <div className="grid size-8 shrink-0 place-items-center rounded-lg border bg-muted/40 text-muted-foreground">
                <Icon className="size-3.5" />
              </div>
              <div className="min-w-0">
                <p className="text-xs font-medium text-muted-foreground">{label}</p>
                <p className="truncate text-xl font-semibold tracking-normal">{value}</p>
                {detail ? <p className="truncate text-xs text-muted-foreground">{detail}</p> : null}
              </div>
            </CardContent>
          </Card>
        ))}
      </div>

      <Card>
        <CardHeader className="gap-3 p-4 sm:flex-row sm:items-center sm:justify-between sm:space-y-0">
          <div>
            <CardTitle>Abgerechneter Aufwand</CardTitle>
            <CardDescription>
              {periodType === "month" ? "Abrechenbare Zeit pro Tag im ausgewählten Monat." : "Abrechenbare Zeit pro Stunde am ausgewählten Tag."}
            </CardDescription>
          </div>
          <div className="flex items-center gap-2">
            <Badge variant="outline">{formatMinutes(totals?.minutes ?? 0)}</Badge>
            <HelpLink anchor="auswertung" label="Hilfe zur Auswertung" />
          </div>
        </CardHeader>
        <CardContent className="px-4 pb-4">
          <div className="h-44 w-full overflow-hidden rounded-md border bg-background">
            {(totals?.minutes ?? 0) > 0 ? (
              <svg viewBox={`0 0 ${chartWidth} ${chartHeight}`} className="h-full w-full">
                {yAxisTicks.map((tick) => {
                  const y = chartPadding.top + (1 - tick / chartMaxMinutes) * chartInnerHeight;

                  return (
                    <g key={tick}>
                      <line x1={chartPadding.left} x2={chartWidth - chartPadding.right} y1={y} y2={y} stroke="currentColor" className="text-muted/70" />
                      <line x1={chartPadding.left - 5} x2={chartPadding.left} y1={y} y2={y} stroke="currentColor" className="text-muted-foreground/70" />
                      <text x={chartPadding.left - 9} y={y + 4} textAnchor="end" className="fill-muted-foreground text-[11px]">
                        {formatAxisMinutes(tick)}
                      </text>
                    </g>
                  );
                })}
                <line
                  x1={chartPadding.left}
                  x2={chartPadding.left}
                  y1={chartPadding.top}
                  y2={chartPadding.top + chartInnerHeight}
                  stroke="currentColor"
                  className="text-muted-foreground/70"
                />

                {chartPoints.map((point) => {
                  const barY = chartPadding.top + chartInnerHeight - point.barHeight;

                  return (
                    <g key={point.bucket.key}>
                      {point.bucket.totalMinutes > 0 ? (
                        <rect
                          x={point.x - barWidth / 2}
                          y={barY}
                          width={barWidth}
                          height={point.barHeight}
                          rx="4"
                          className="fill-neutral-300 dark:fill-neutral-700"
                        />
                      ) : null}
                    </g>
                  );
                })}

                {effortPath ? <path d={effortPath} fill="none" stroke="currentColor" strokeWidth="2.5" className="text-foreground" /> : null}

                {chartPoints.filter((point) => point.bucket.totalMinutes > 0).map((point) => (
                  <circle key={`${point.bucket.key}-effort`} cx={point.x} cy={point.y} r="3" className="fill-background stroke-foreground" strokeWidth="2" />
                ))}

                {chartPoints.map((point, index) =>
                  index % labelStep === 0 || index === chartPoints.length - 1 ? (
                    <text key={`${point.bucket.key}-label`} x={point.x} y={chartHeight - 10} textAnchor="middle" className="fill-muted-foreground text-[11px]">
                      {point.bucket.label}
                    </text>
                  ) : null
                )}
              </svg>
            ) : (
              <div className="grid h-full place-items-center text-sm text-muted-foreground">Keine Daten für den gewählten Zeitraum.</div>
            )}
          </div>
          <div className="mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground">
            <span className="inline-flex items-center gap-1.5">
              <span className="size-2 rounded-sm bg-neutral-300 dark:bg-neutral-700" />
              Abgerechnet
            </span>
            <span className="inline-flex items-center gap-1.5">
              <span className="h-0.5 w-4 bg-foreground" />
              Verlauf
            </span>
          </div>
        </CardContent>
      </Card>

      <Card>
        <CardHeader className="gap-3 p-4 sm:flex-row sm:items-center sm:justify-between sm:space-y-0">
          <div>
            <CardTitle>Tickets im Zeitraum</CardTitle>
            <CardDescription>
              {periodType === "month" ? "Sessions prüfen, bewerten und bei Bedarf Tickets öffnen." : "Tagesansicht ohne eigenen Abschluss."}
            </CardDescription>
          </div>
          <HelpLink anchor="auswertung" label="Hilfe zu Tickets im Zeitraum" />
        </CardHeader>
        <CardContent className="px-4 pb-4">
          <div className="mb-4 rounded-md border bg-muted/20 p-3">
            <div className="mb-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
              <div className="flex items-center gap-2">
                <SlidersHorizontal className="size-4 text-muted-foreground" />
                <div>
                  <p className="text-sm font-medium">Ansicht</p>
                  <p className="text-xs text-muted-foreground">
                    {visibleTickets.length} von {tickets.length} Ticket(s)
                  </p>
                </div>
                {activeFilterCount > 0 ? <Badge variant="outline">{activeFilterCount} aktiv</Badge> : null}
                <HelpLink anchor="auswertung" label="Hilfe zu Filtern und Sortierung" />
              </div>
              <Button
                type="button"
                size="sm"
                variant="secondary"
                disabled={activeFilterCount === 0}
                onClick={() => setTicketViewSettings(defaultTicketViewSettings)}
              >
                <RotateCcw className="size-3.5" />
                Zurücksetzen
              </Button>
            </div>

            <div className="grid gap-2 md:grid-cols-2 xl:grid-cols-[minmax(180px,1fr)_150px_190px_140px_140px_140px_120px]">
              <div className="space-y-1">
                <label className="text-xs font-medium text-muted-foreground" htmlFor="ticket-filter-query">Suche</label>
                <Input
                  id="ticket-filter-query"
                  className="h-8"
                  value={ticketViewSettings.query}
                  placeholder="Ticket oder Organisation"
                  onChange={(event) => setTicketViewSettings((current) => ({ ...current, query: event.currentTarget.value }))}
                />
              </div>

              <div className="space-y-1">
                <label className="text-xs font-medium text-muted-foreground">Typ</label>
                <Select value={ticketViewSettings.workType} onValueChange={(value) => setTicketViewSettings((current) => ({ ...current, workType: value as TicketWorkTypeFilter }))}>
                  <SelectTrigger size="sm" className="w-full">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="all">Alle Typen</SelectItem>
                    <SelectItem value="support">Support</SelectItem>
                    <SelectItem value="consulting">Consulting</SelectItem>
                  </SelectContent>
                </Select>
              </div>

              <div className="space-y-1">
                <label className="text-xs font-medium text-muted-foreground">Organisation</label>
                <Select value={ticketViewSettings.organization} onValueChange={(value) => setTicketViewSettings((current) => ({ ...current, organization: value }))}>
                  <SelectTrigger size="sm" className="w-full">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="all">Alle Organisationen</SelectItem>
                    {organizationOptions.map((organization) => (
                      <SelectItem key={organization.value} value={organization.value}>
                        {organization.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>

              <div className="space-y-1">
                <label className="text-xs font-medium text-muted-foreground">Status</label>
                <Select value={ticketViewSettings.status} onValueChange={(value) => setTicketViewSettings((current) => ({ ...current, status: value as TicketStatusFilter }))}>
                  <SelectTrigger size="sm" className="w-full">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="all">Alle Status</SelectItem>
                    <SelectItem value="open">Offen</SelectItem>
                    <SelectItem value="evaluated">Bewertet</SelectItem>
                  </SelectContent>
                </Select>
              </div>

              <div className="space-y-1">
                <label className="text-xs font-medium text-muted-foreground">Gruppierung</label>
                <Select value={ticketViewSettings.groupBy} onValueChange={(value) => setTicketViewSettings((current) => ({ ...current, groupBy: value as TicketGroupBy }))}>
                  <SelectTrigger size="sm" className="w-full">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {groupByOptions.map((option) => (
                      <SelectItem key={option.value} value={option.value}>
                        {option.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>

              <div className="space-y-1">
                <label className="text-xs font-medium text-muted-foreground">Sortierung</label>
                <Select value={ticketViewSettings.sortBy} onValueChange={(value) => setTicketViewSettings((current) => ({ ...current, sortBy: value as TicketSortBy }))}>
                  <SelectTrigger size="sm" className="w-full">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {sortByOptions.map((option) => (
                      <SelectItem key={option.value} value={option.value}>
                        {option.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>

              <div className="space-y-1">
                <label className="text-xs font-medium text-muted-foreground">Richtung</label>
                <Select value={ticketViewSettings.sortDirection} onValueChange={(value) => setTicketViewSettings((current) => ({ ...current, sortDirection: value as TicketSortDirection }))}>
                  <SelectTrigger size="sm" className="w-full">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="asc">Aufsteigend</SelectItem>
                    <SelectItem value="desc">Absteigend</SelectItem>
                  </SelectContent>
                </Select>
              </div>
            </div>
          </div>

          <div className="hidden md:block">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>Ticket</TableHead>
                  <TableHead>Organisation</TableHead>
                  <TableHead>Art</TableHead>
                  <TableHead>Sessions</TableHead>
                  <TableHead>Zeit abgerechnet</TableHead>
                  <TableHead>Sessions abgerechnet</TableHead>
                  <TableHead>Nicht abrechenbar</TableHead>
                  <TableHead>Status</TableHead>
                  <TableHead />
                </TableRow>
              </TableHeader>
              <TableBody>
                {ticketSections.map((section, sectionIndex) => (
                  <Fragment key={section.key}>
                    {ticketViewSettings.groupBy !== "none" ? (
                      <TableRow className={`${sectionIndex > 0 ? "border-t-[12px] border-t-background" : ""} border-b-2 border-border bg-muted/70 hover:bg-muted/70`}>
                        <TableCell colSpan={9} className="py-0">
                          <div className="flex flex-wrap items-center justify-between gap-3 rounded-md border border-border/80 bg-card px-3 py-2 shadow-sm">
                            <div className="flex min-w-0 items-center gap-2">
                              <span className="rounded-sm bg-primary px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary-foreground">Gruppe</span>
                              <span className="truncate text-base font-semibold">{section.label}</span>
                            </div>
                            <div className="flex flex-wrap items-center gap-2">
                              <Badge variant="outline">{section.ticketCount} Ticket(s)</Badge>
                              <Badge variant="outline">{formatMinutes(section.totalMinutes)}</Badge>
                              {section.openCount > 0 ? <Badge variant="warning">{section.openCount} offen</Badge> : <Badge variant="success">bewertet</Badge>}
                            </div>
                          </div>
                        </TableCell>
                      </TableRow>
                    ) : null}
                    {section.tickets.map((ticket) => (
                      <TableRow key={ticket.id}>
                        <TableCell>
                          <div className="flex items-center gap-1">
                            <span className="font-semibold">{ticket.ticket_number}</span>
                            <CopyTicketButton ticketNumber={ticket.ticket_number} />
                          </div>
                        </TableCell>
                        <TableCell>{ticket.customer_name ?? "-"}</TableCell>
                        <TableCell>{workTypeLabel(ticket.work_type)}</TableCell>
                        <TableCell>{ticket.session_count}</TableCell>
                        <TableCell>{formatMinutes(ticket.total_minutes)}</TableCell>
                        <TableCell>{ticket.billed_count}</TableCell>
                        <TableCell>{ticket.non_billable_count} / {formatMinutes(ticket.non_billable_minutes)}</TableCell>
                        <TableCell>
                          <TicketStatus ticket={ticket} />
                        </TableCell>
                        <TableCell className="text-right">
                          <Button variant="ghost" size="sm" onClick={() => onNavigate(`/analysis/${periodType}/${period}/tickets/${ticket.id}`)}>
                            Öffnen
                            <ExternalLink className="size-4" />
                          </Button>
                        </TableCell>
                      </TableRow>
                    ))}
                  </Fragment>
                ))}
              </TableBody>
            </Table>
          </div>

          <div className="space-y-2 md:hidden">
            {ticketSections.map((section, sectionIndex) => (
              <div key={section.key} className={`space-y-2 ${ticketViewSettings.groupBy !== "none" && sectionIndex > 0 ? "pt-3" : ""}`}>
                {ticketViewSettings.groupBy !== "none" ? (
                  <div className="rounded-md border-2 border-border bg-card px-3 py-2 shadow-sm">
                    <div className="mb-2 flex min-w-0 items-center gap-2">
                      <span className="rounded-sm bg-primary px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary-foreground">Gruppe</span>
                      <span className="truncate text-base font-semibold">{section.label}</span>
                    </div>
                    <div className="flex flex-wrap items-center gap-2">
                      <Badge variant="outline">{section.ticketCount} Ticket(s)</Badge>
                      <Badge variant="outline">{formatMinutes(section.totalMinutes)}</Badge>
                      {section.openCount > 0 ? <Badge variant="warning">{section.openCount} offen</Badge> : <Badge variant="success">bewertet</Badge>}
                    </div>
                  </div>
                ) : null}
                {section.tickets.map((ticket) => (
                  <div key={ticket.id} className="space-y-2 rounded-md border bg-background p-3">
                    <div className="flex items-start justify-between gap-3">
                      <div className="min-w-0">
                        <div className="flex items-center gap-1">
                          <p className="truncate font-semibold">{ticket.ticket_number}</p>
                          <CopyTicketButton ticketNumber={ticket.ticket_number} />
                        </div>
                        <p className="text-sm text-muted-foreground">{ticket.customer_name ?? "Keine Organisation"} · {workTypeLabel(ticket.work_type)} · {ticket.session_count} Session(s)</p>
                      </div>
                      <TicketStatus ticket={ticket} />
                    </div>
                    <div className="grid grid-cols-3 gap-2 text-sm">
                      <div className="rounded-md bg-muted/50 p-2">
                        <p className="text-muted-foreground">Abr.</p>
                        <p className="font-medium">{formatMinutes(ticket.total_minutes)}</p>
                      </div>
                      <div className="rounded-md bg-muted/50 p-2">
                        <p className="text-muted-foreground">Abr.</p>
                        <p className="font-medium">{ticket.billed_count}</p>
                      </div>
                      <div className="rounded-md bg-muted/50 p-2">
                        <p className="text-muted-foreground">Nicht</p>
                        <p className="font-medium">{formatMinutes(ticket.non_billable_minutes)}</p>
                      </div>
                    </div>
                    <Button className="w-full" size="sm" variant="secondary" onClick={() => onNavigate(`/analysis/${periodType}/${period}/tickets/${ticket.id}`)}>
                      Öffnen
                      <ExternalLink className="size-4" />
                    </Button>
                  </div>
                ))}
              </div>
            ))}
          </div>

          {tickets.length === 0 ? (
            <p className="py-8 text-center text-sm text-muted-foreground">Für diesen Zeitraum sind noch keine Sessions vorhanden.</p>
          ) : visibleTickets.length === 0 ? (
            <p className="py-8 text-center text-sm text-muted-foreground">Keine Tickets passend zu den aktuellen Filtern.</p>
          ) : null}
        </CardContent>
      </Card>
    </div>
  );
}
