import type { ReactNode } from "react";
import { Avatar, AvatarFallback } from "./avatar";
import { Badge } from "./badge";
import { Button } from "./button";
import { Separator } from "./separator";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs";
import { ChevronLeft, ChevronRight } from "lucide-react";
import type { AppointmentStatus } from "./appointment-time-slot-picker";
import { formatWeekdayShort } from "@/lib/format-date";

export type { AppointmentStatus } from "./appointment-time-slot-picker";

export interface AppointmentCalendarItem {
  id: string;
  status: AppointmentStatus;
  clientName: string;
  clientAvatarInitials: string;
  /** ISO date string — "2026-04-22" */
  date: string;
  /** Display time — "10:00 AM" */
  timeStart: string;
  /** Display time — "10:30 AM" */
  timeEnd: string;
}

export interface AppointmentWeekDay {
  /** Short weekday label — "Mon" */
  label: string;
  /** ISO date string — "2026-04-20" */
  date: string;
  /** Short date number — "20" */
  short: string;
}

export interface AppointmentCalendarViewProps {
  appointments: AppointmentCalendarItem[];
  /** ISO date string for the "today" highlight — "2026-04-22" */
  today: string;
  /** Label shown in the toolbar — "April 2026" */
  periodLabel: string;
  /** Days shown in week view */
  weekDays: AppointmentWeekDay[];
  /** Hours displayed in day/week view — defaults to [9..17] */
  hours?: number[];
  /** ISO date controlling which day/month is displayed — defaults to `today` */
  viewDate?: string;
  /** @deprecated Use `viewDate` instead */
  dayViewDate?: string;
  defaultView?: "day" | "week" | "month";
  onPrev?: () => void;
  onNext?: () => void;
  onToday?: () => void;
  onViewChange?: (view: "day" | "week" | "month") => void;
  onSelectAppointment?: (apt: AppointmentCalendarItem) => void;
  /** Slot rendered in the calendar toolbar right side */
  toolbarActions?: ReactNode;
}

// ---------------------------------------------------------------------------
// Status config
// ---------------------------------------------------------------------------

const STATUS: Record<
  AppointmentStatus,
  {
    card: string;
    badge: "warning" | "success" | "destructive" | "info";
    label: string;
  }
> = {
  pending: {
    card: "border-l-warning bg-warning/[0.06]",
    badge: "warning",
    label: "Pending",
  },
  confirmed: {
    card: "border-l-success bg-success/[0.06]",
    badge: "success",
    label: "Confirmed",
  },
  cancelled: {
    card: "border-l-destructive bg-destructive/[0.06]",
    badge: "destructive",
    label: "Cancelled",
  },
  rescheduled: {
    card: "border-l-info bg-info/[0.06]",
    badge: "info",
    label: "Rescheduled",
  },
};

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function formatHour(h: number) {
  if (h === 12) return "12 PM";
  return h < 12 ? `${h} AM` : `${h - 12} PM`;
}

function getHourFromTime(time: string): number {
  const match = time.match(/^(\d+):/);
  if (!match) return 0;
  const h = parseInt(match[1], 10);
  return time.includes("PM") && h !== 12 ? h + 12 : h;
}

const DEFAULT_HOURS = [9, 10, 11, 12, 13, 14, 15, 16, 17];

// ---------------------------------------------------------------------------
// Shared sub-components
// ---------------------------------------------------------------------------

function TimeGutter({ hour }: { hour: number }) {
  return (
    <div className="flex w-16 shrink-0 items-start justify-end border-r border-border pr-3 pt-2">
      <span className="text-caption text-muted-foreground">
        {formatHour(hour)}
      </span>
    </div>
  );
}

function DayCard({
  apt,
  onSelect,
}: {
  apt: AppointmentCalendarItem;
  onSelect?: (apt: AppointmentCalendarItem) => void;
}) {
  return (
    <Button
      type="button"
      variant="ghost"
      onClick={() => onSelect?.(apt)}
      className={`h-auto w-full justify-start gap-3 border-l-2 px-3 py-3 text-left hover:opacity-80 ${STATUS[apt.status].card}`}
    >
      <Avatar className="h-8 w-8 shrink-0">
        <AvatarFallback className="text-caption">
          {apt.clientAvatarInitials}
        </AvatarFallback>
      </Avatar>
      <div className="flex min-w-0 flex-1 flex-col gap-0.5">
        <span className="text-body-small font-semibold">{apt.clientName}</span>
        <span className="text-caption text-muted-foreground">
          {apt.timeStart} – {apt.timeEnd}
        </span>
      </div>
      <Badge variant={STATUS[apt.status].badge}>
        {STATUS[apt.status].label}
      </Badge>
    </Button>
  );
}

function AptChip({
  apt,
  onSelect,
  className,
}: {
  apt: AppointmentCalendarItem;
  onSelect?: (apt: AppointmentCalendarItem) => void;
  className?: string;
}) {
  return (
    <Button
      type="button"
      variant="ghost"
      onClick={() => onSelect?.(apt)}
      className={`h-auto w-full justify-start truncate border-l-2 text-left hover:opacity-80 ${STATUS[apt.status].card} ${className ?? ""}`}
    >
      <span className="truncate text-caption font-medium">
        {apt.timeStart} {apt.clientName}
      </span>
    </Button>
  );
}

// ---------------------------------------------------------------------------
// Day view
// ---------------------------------------------------------------------------

function DayView({
  appointments,
  date,
  today,
  hours,
  onSelectAppointment,
}: {
  appointments: AppointmentCalendarItem[];
  date: string;
  today: string;
  hours: number[];
  onSelectAppointment?: (apt: AppointmentCalendarItem) => void;
}) {
  const d = new Date(date + "T00:00:00");
  const isToday = date === today;
  const dayApts = appointments.filter((a) => a.date === date);

  return (
    <div className="flex flex-1 flex-col">
      <div className="flex shrink-0 border-b border-border">
        <div className="w-16 shrink-0 border-r border-border" />
        <div className="flex flex-1 flex-col items-center gap-1 py-4">
          <span
            className={`text-caption tracking-wide ${isToday ? "font-semibold text-primary" : "text-muted-foreground"}`}
          >
            {formatWeekdayShort(d)}
          </span>
          <span
            className={`flex h-10 w-10 items-center justify-center text-xl font-semibold ${isToday ? "text-primary" : ""}`}
          >
            {d.getDate()}
          </span>
        </div>
      </div>

      <div className="flex flex-1 flex-col">
        {hours.map((hour) => {
          const aptsAtHour = dayApts.filter(
            (a) => getHourFromTime(a.timeStart) === hour,
          );
          return (
            <div
              key={hour}
              className="flex flex-1 border-b border-border/40 last:border-b-0"
            >
              <TimeGutter hour={hour} />
              <div className="flex flex-1 flex-col gap-2 p-2">
                {aptsAtHour.map((apt) => (
                  <DayCard
                    key={apt.id}
                    apt={apt}
                    onSelect={onSelectAppointment}
                  />
                ))}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Week view
// ---------------------------------------------------------------------------

function WeekView({
  appointments,
  weekDays,
  today,
  hours,
  onSelectAppointment,
}: {
  appointments: AppointmentCalendarItem[];
  weekDays: AppointmentWeekDay[];
  today: string;
  hours: number[];
  onSelectAppointment?: (apt: AppointmentCalendarItem) => void;
}) {
  return (
    <div className="flex flex-1 flex-col overflow-x-auto">
      <div className="flex shrink-0 border-b border-border">
        <div className="w-16 shrink-0 border-r border-border" />
        {weekDays.map((day) => (
          <div
            key={day.date}
            className="flex flex-1 flex-col items-center border-r border-border py-3 last:border-r-0"
          >
            <span
              className={`text-caption ${day.date === today ? "font-semibold text-primary" : "text-muted-foreground"}`}
            >
              {day.label}
            </span>
            <span
              className={`text-body-small font-semibold ${day.date === today ? "text-primary" : ""}`}
            >
              {day.short}
            </span>
          </div>
        ))}
      </div>

      <div className="flex flex-1 flex-col">
        {hours.map((hour) => (
          <div
            key={hour}
            className="flex flex-1 border-b border-border/40 last:border-b-0"
          >
            <TimeGutter hour={hour} />
            {weekDays.map((day) => {
              const aptsAtCell = appointments.filter(
                (a) =>
                  a.date === day.date && getHourFromTime(a.timeStart) === hour,
              );
              return (
                <div
                  key={day.date}
                  className="flex flex-1 flex-col overflow-hidden border-r border-border/40 p-1 last:border-r-0"
                >
                  {aptsAtCell.map((apt) => (
                    <AptChip
                      key={apt.id}
                      apt={apt}
                      onSelect={onSelectAppointment}
                      className="px-2 py-1.5"
                    />
                  ))}
                </div>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Month view
// ---------------------------------------------------------------------------

const MONTH_DAY_HEADERS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];

function generateMonthGrid(year: number, month: number): (Date | null)[][] {
  const firstDay = new Date(year, month, 1);
  const lastDay = new Date(year, month + 1, 0);
  const startDow = firstDay.getDay();
  const grid: (Date | null)[][] = [];
  let week: (Date | null)[] = Array.from({ length: startDow }, () => null);

  for (let day = 1; day <= lastDay.getDate(); day++) {
    week.push(new Date(year, month, day));
    if (week.length === 7) {
      grid.push(week);
      week = [];
    }
  }
  if (week.length > 0) {
    while (week.length < 7) week.push(null);
    grid.push(week);
  }
  return grid;
}

function MonthView({
  appointments,
  viewDate,
  today,
  onSelectAppointment,
}: {
  appointments: AppointmentCalendarItem[];
  viewDate: string;
  today: string;
  onSelectAppointment?: (apt: AppointmentCalendarItem) => void;
}) {
  const vd = new Date(viewDate + "T00:00:00");
  const grid = generateMonthGrid(vd.getFullYear(), vd.getMonth());
  // Use local date parts — toISOString() converts to UTC first, which shifts the
  // date back 1 day for UTC+ timezones (e.g. UTC+7 shows Thu booking on Wed cell).
  const toIso = (d: Date) =>
    `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;

  return (
    <div className="flex flex-1 flex-col">
      <div className="grid shrink-0 grid-cols-7 border-b border-border">
        {MONTH_DAY_HEADERS.map((d) => (
          <div
            key={d}
            className="flex items-center justify-center border-r border-border/40 py-2 last:border-r-0"
          >
            <span className="text-caption font-medium text-muted-foreground">
              {d}
            </span>
          </div>
        ))}
      </div>

      <div className="flex flex-1 flex-col">
        {grid.map((week, wi) => (
          <div
            key={wi}
            className="flex flex-1 border-b border-border last:border-b-0"
          >
            {week.map((day, di) => {
              const iso = day ? toIso(day) : null;
              const dayApts = iso
                ? appointments.filter((a) => a.date === iso)
                : [];
              const isToday = iso === today;

              return (
                <div
                  key={di}
                  className={`min-w-0 flex-1 border-r border-border/40 p-1.5 last:border-r-0 ${!day ? "bg-muted/20" : ""}`}
                >
                  {day && (
                    <>
                      <div
                        className={`mb-1 flex h-6 w-6 items-center justify-center text-xs font-semibold ${isToday ? "text-primary" : "text-foreground"}`}
                      >
                        {day.getDate()}
                      </div>
                      <div className="flex flex-col gap-0.5">
                        {dayApts.slice(0, 2).map((apt) => (
                          <AptChip
                            key={apt.id}
                            apt={apt}
                            onSelect={onSelectAppointment}
                            className="px-1.5 py-1"
                          />
                        ))}
                        {dayApts.length > 2 && (
                          <p className="px-1 text-caption text-muted-foreground">
                            +{dayApts.length - 2} more
                          </p>
                        )}
                      </div>
                    </>
                  )}
                </div>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Composite calendar view
// ---------------------------------------------------------------------------

export function AppointmentCalendarView({
  appointments,
  today,
  periodLabel,
  weekDays,
  hours = DEFAULT_HOURS,
  viewDate,
  dayViewDate,
  defaultView = "week",
  onPrev,
  onNext,
  onToday,
  onViewChange,
  onSelectAppointment,
  toolbarActions,
}: AppointmentCalendarViewProps) {
  const effectiveViewDate = viewDate ?? dayViewDate ?? today;

  return (
    <div className="flex flex-1 flex-col min-h-0 border border-border bg-card">
      <div className="flex items-center justify-between gap-4 px-4 py-3">
        <div className="flex items-center gap-2">
          <Button
            variant="outline"
            size="sm"
            className="gap-1"
            onClick={onPrev}
          >
            <ChevronLeft className="h-3.5 w-3.5" />
          </Button>
          <p className="text-label-medium">{periodLabel}</p>
          <Button
            variant="outline"
            size="sm"
            className="gap-1"
            onClick={onNext}
          >
            <ChevronRight className="h-3.5 w-3.5" />
          </Button>
          <Button variant="outline" size="sm" onClick={onToday}>
            Today
          </Button>
        </div>
        {toolbarActions && (
          <div className="flex items-center gap-2">{toolbarActions}</div>
        )}
      </div>

      <Separator />

      <Tabs
        defaultValue={defaultView}
        onValueChange={(v) => onViewChange?.(v as "day" | "week" | "month")}
        className="flex flex-col flex-1 min-h-0"
      >
        <div className="px-4 pt-3">
          <TabsList>
            <TabsTrigger value="day">Day</TabsTrigger>
            <TabsTrigger value="week">Week</TabsTrigger>
            <TabsTrigger value="month">Month</TabsTrigger>
          </TabsList>
        </div>
        <TabsContent
          value="day"
          className="mt-0 min-h-0 flex-1 flex flex-col overflow-y-auto"
        >
          <DayView
            appointments={appointments}
            date={effectiveViewDate}
            today={today}
            hours={hours}
            onSelectAppointment={onSelectAppointment}
          />
        </TabsContent>
        <TabsContent
          value="week"
          className="mt-0 min-h-0 flex-1 flex flex-col overflow-y-auto"
        >
          <WeekView
            appointments={appointments}
            weekDays={weekDays}
            today={today}
            hours={hours}
            onSelectAppointment={onSelectAppointment}
          />
        </TabsContent>
        <TabsContent
          value="month"
          className="mt-0 min-h-0 flex-1 flex flex-col overflow-y-auto"
        >
          <MonthView
            appointments={appointments}
            viewDate={effectiveViewDate}
            today={today}
            onSelectAppointment={onSelectAppointment}
          />
        </TabsContent>
      </Tabs>
    </div>
  );
}
