"use client";

import { useEffect, useMemo, useState } from "react";
import axiosInstance from "@/lib/axiosInstance";
import { WEEKDAYS, chunkTimeRange, fmt12 } from "@/lib/timeslot-utils";
import { SLOT_DURATION_MINUTES, DEFAULT_SLOT_CAPACITY } from "@/lib/timeslot-constants";
import Link from "next/link";

interface Session { start_time: string; end_time: string; }
interface DayConfig { weekday: number; is_closed: boolean; sessions: Session[]; }

interface GeneratedSlot {
  id: number;
  slot_date: string;
  start_time: string;
  end_time: string;
  capacity: number;
  booked_count: number;
}

interface MasterRecord {
  id: number;
  start_date: string;
  end_date: string;
}

const defaultDays: DayConfig[] = WEEKDAYS.map((_, weekday) => ({
  weekday,
  is_closed: weekday === 0,
  sessions:
    weekday === 0
      ? []
      : weekday === 6
        ? [{ start_time: "11:00", end_time: "17:00" }]
        : [
          { start_time: "10:00", end_time: "14:00" },
          { start_time: "15:00", end_time: "21:00" },
        ],
}));

// UTC-safe date enumeration — matches the pattern used elsewhere in the app
// (parseDateUTC / enumerateDates) so this never drifts a day off in +/- tz offsets.
function enumerateDates(start: string, end: string): string[] {
  const out: string[] = [];
  const cursor = new Date(start + "T00:00:00Z");
  const endD = new Date(end + "T00:00:00Z");
  while (cursor.getTime() <= endD.getTime()) {
    out.push(cursor.toISOString().slice(0, 10));
    cursor.setUTCDate(cursor.getUTCDate() + 1);
  }
  return out;
}

// Merge overlapping/adjacent master batches into continuous segments (masters are
// often created in separate batches), then a picked range only counts as a real
// conflict if it overlaps one of these merged segments.
function mergeMasterRanges(list: { start_date: string; end_date: string }[]) {
  const sorted = list
    .map((m) => ({ start: m.start_date.slice(0, 10), end: m.end_date.slice(0, 10) }))
    .sort((a, b) => (a.start < b.start ? -1 : a.start > b.start ? 1 : 0));

  const merged: { start: string; end: string }[] = [];
  for (const r of sorted) {
    const last = merged[merged.length - 1];
    if (last && r.start <= last.end) {
      if (r.end > last.end) last.end = r.end;
    } else {
      merged.push({ start: r.start, end: r.end });
    }
  }
  return merged;
}

function rangesOverlap(aStart: string, aEnd: string, bStart: string, bEnd: string) {
  return aStart <= bEnd && aEnd >= bStart;
}

export default function TimeSlotsMasterPage() {
  const [startDate, setStartDate] = useState("");
  const [endDate, setEndDate] = useState("");
  const [days, setDays] = useState<DayConfig[]>(defaultDays);
  const [loading, setLoading] = useState(false);
  const [statusMsg, setStatusMsg] = useState<string | null>(null);
  const [statusIsError, setStatusIsError] = useState(false);

  const [calendarLoading, setCalendarLoading] = useState(false);
  const [slotsByDate, setSlotsByDate] = useState<Record<string, GeneratedSlot[]>>({});
  const [visibleMonth, setVisibleMonth] = useState(() => {
    const now = new Date();
    return new Date(now.getFullYear(), now.getMonth(), 1);
  });
  const [selectedDate, setSelectedDate] = useState<string | null>(null);
  const [masters, setMasters] = useState<MasterRecord[]>([]);

  const todayStr = useMemo(() => new Date().toISOString().slice(0, 10), []);

  const loadAllGeneratedSlots = async () => {
    setCalendarLoading(true);
    try {
      const mastersRes = await axiosInstance.get("/admin/timeslots/masters");
      const masterList: MasterRecord[] = mastersRes.data.data;
      setMasters(masterList);

      if (!masterList.length) {
        setSlotsByDate({});
        return;
      }

      const earliestStart = masterList.reduce((min, m) => (m.start_date < min ? m.start_date : min), masterList[0].start_date);
      const latestEnd = masterList.reduce((max, m) => (m.end_date > max ? m.end_date : max), masterList[0].end_date);

      const rangeRes = await axiosInstance.get(
        `/admin/timeslots/range?start_date=${earliestStart.slice(0, 10)}&end_date=${latestEnd.slice(0, 10)}`
      );
      const slots: GeneratedSlot[] = rangeRes.data.data;

      const grouped: Record<string, GeneratedSlot[]> = {};
      for (const slot of slots) {
        const dateKey = slot.slot_date.slice(0, 10);
        if (!grouped[dateKey]) grouped[dateKey] = [];
        grouped[dateKey].push(slot);
      }
      setSlotsByDate(grouped);
    } catch {
      // keep whatever was already loaded
    } finally {
      setCalendarLoading(false);
    }
  };

  useEffect(() => {
    loadAllGeneratedSlots();
  }, []);

  // ---- Overlap validation against existing masters ----
  const mergedMasterRanges = useMemo(() => mergeMasterRanges(masters), [masters]);

  const overlapError = useMemo(() => {
    if (!startDate || !endDate) return null;
    if (endDate < startDate) return "End date must be after start date.";
    const conflict = mergedMasterRanges.find((r) => rangesOverlap(startDate, endDate, r.start, r.end));
    if (!conflict) return null;
    return `This range overlaps with existing slots (${conflict.start} to ${conflict.end}). Pick a non-overlapping range.`;
  }, [startDate, endDate, mergedMasterRanges]);

  // ---- Which weekdays actually fall inside the picked range ----
  // null = no range picked yet -> show full weekly config so admins can set
  // up everything before choosing dates. Once both dates are picked, only the
  // weekdays that actually occur in that span are shown (e.g. a 3-day span
  // only touches 2-3 distinct weekdays, not all 7).
  const weekdaysInRange = useMemo(() => {
    if (!startDate || !endDate || endDate < startDate) return null;
    const set = new Set<number>();
    for (const d of enumerateDates(startDate, endDate)) {
      set.add(new Date(d + "T00:00:00Z").getUTCDay());
    }
    return set;
  }, [startDate, endDate]);

  const visibleDays = useMemo(
    () => (weekdaysInRange ? days.filter((d) => weekdaysInRange.has(d.weekday)) : days),
    [days, weekdaysInRange]
  );

  const updateDay = (weekday: number, patch: Partial<DayConfig>) => {
    setDays((prev) => prev.map((d) => (d.weekday === weekday ? { ...d, ...patch } : d)));
  };

  const updateSession = (weekday: number, sessionIndex: number, patch: Partial<Session>) => {
    setDays((prev) =>
      prev.map((d) =>
        d.weekday !== weekday
          ? d
          : { ...d, sessions: d.sessions.map((s, i) => (i === sessionIndex ? { ...s, ...patch } : s)) }
      )
    );
  };

  const addSession = (weekday: number) => {
    updateDay(weekday, {
      sessions: [...(days.find((d) => d.weekday === weekday)?.sessions ?? []), { start_time: "00:00", end_time: "00:00" }],
    });
  };

  const removeSession = (weekday: number, sessionIndex: number) => {
    const day = days.find((d) => d.weekday === weekday);
    if (!day) return;
    updateDay(weekday, { sessions: day.sessions.filter((_, i) => i !== sessionIndex) });
  };

  // Total slots that will actually be generated for the picked range — counts
  // how many times each visible weekday occurs in the span, not just "per week".
  const slotsForRange = useMemo(() => {
    if (!startDate || !endDate || endDate < startDate) return null;
    const occurrences: Record<number, number> = {};
    for (const d of enumerateDates(startDate, endDate)) {
      const wd = new Date(d + "T00:00:00Z").getUTCDay();
      occurrences[wd] = (occurrences[wd] ?? 0) + 1;
    }
    let total = 0;
    for (const day of visibleDays) {
      if (day.is_closed) continue;
      const perOccurrence = day.sessions.reduce(
        (s, session) => s + chunkTimeRange(session.start_time, session.end_time, SLOT_DURATION_MINUTES).length,
        0
      );
      total += perOccurrence * (occurrences[day.weekday] ?? 0);
    }
    return total;
  }, [startDate, endDate, visibleDays]);

  const weeklySlotCount = days.reduce(
    (total, d) =>
      total +
      (d.is_closed
        ? 0
        : d.sessions.reduce((s, session) => s + chunkTimeRange(session.start_time, session.end_time, SLOT_DURATION_MINUTES).length, 0)),
    0
  );

  const handleSubmit = async () => {
    if (!startDate || !endDate) {
      setStatusIsError(true);
      setStatusMsg("Pick a start and end date.");
      return;
    }
    if (startDate < todayStr) {
      setStatusIsError(true);
      setStatusMsg("Start date can't be in the past.");
      return;
    }
    if (endDate < startDate) {
      setStatusIsError(true);
      setStatusMsg("End date must be after start date.");
      return;
    }
    if (overlapError) {
      setStatusIsError(true);
      setStatusMsg(overlapError);
      return;
    }

    setLoading(true);
    setStatusMsg(null);
    try {
      const { data } = await axiosInstance.post("/admin/timeslots/masters", { start_date: startDate, end_date: endDate, days });
      await axiosInstance.post(`/admin/timeslots/masters/${data.data.id}/generate`);
      setStatusIsError(false);
      setStatusMsg("Master slots created and generated successfully.");
      await loadAllGeneratedSlots();
      setVisibleMonth(new Date(new Date(startDate).getFullYear(), new Date(startDate).getMonth(), 1));
      setStartDate("");
      setEndDate("");
    } catch (err: any) {
      setStatusIsError(true);
      setStatusMsg(err?.response?.data?.error ?? "Failed to create master slots. Please try again.");
    } finally {
      setLoading(false);
    }
  };

  // --- Calendar grid math ---
  const calendarCells = useMemo(() => {
    const year = visibleMonth.getFullYear();
    const month = visibleMonth.getMonth();
    const firstDayOfMonth = new Date(year, month, 1);
    const daysInMonth = new Date(year, month + 1, 0).getDate();
    const leadingBlanks = firstDayOfMonth.getDay();

    const cells: { dateKey: string | null; dayNumber: number | null }[] = [];
    for (let i = 0; i < leadingBlanks; i++) cells.push({ dateKey: null, dayNumber: null });
    for (let d = 1; d <= daysInMonth; d++) {
      const dateKey = `${year}-${String(month + 1).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
      cells.push({ dateKey, dayNumber: d });
    }
    return cells;
  }, [visibleMonth]);

  const monthLabel = visibleMonth.toLocaleDateString(undefined, { month: "long", year: "numeric" });
  const goPrevMonth = () => setVisibleMonth((m) => new Date(m.getFullYear(), m.getMonth() - 1, 1));
  const goNextMonth = () => setVisibleMonth((m) => new Date(m.getFullYear(), m.getMonth() + 1, 1));
  const goToday = () => {
    const now = new Date();
    setVisibleMonth(new Date(now.getFullYear(), now.getMonth(), 1));
  };

  const selectedSlots = selectedDate ? (slotsByDate[selectedDate] ?? []) : [];

  return (
    <div className="admin-page-wrapper">
      <div className="admin-page-header mb-4">
        <div className="d-flex justify-content-between align-items-center">
          <div>
            <h2 className="admin-page-title">Master Time Slots</h2>
            <p className="admin-page-subtitle">Manage all master time slots here</p>
          </div>
          <div className="d-flex gap-2 flex-wrap">
            <Link href="/admin/schedule/create" className="admin-theme-btn">
              <i className="bi bi-calendar-check"></i> Slot Assignment
            </Link>
            <Link href="/admin/timeslots/edit-date" className="admin-theme-btn">
              <i className="bi bi-pencil-square"></i> Edit Slot
            </Link>
          </div>
        </div>
      </div>

      <div className="card-theme p-4 mb-4">
        <div className="row g-4">
          <div className="col-md-6">
            <label className="form-label">Start date</label>
            <input
              type="date"
              className="form-control"
              min={todayStr}
              value={startDate}
              onChange={(e) => {
                setStartDate(e.target.value);
                // if end date is now before the new start date, clear it so
                // the user isn't left with an invalid, invisible range
                if (endDate && endDate < e.target.value) setEndDate("");
              }}
            />
          </div>
          <div className="col-md-6">
            <label className="form-label">End date</label>
            <input
              type="date"
              className="form-control"
              min={startDate || todayStr}
              value={endDate}
              onChange={(e) => setEndDate(e.target.value)}
              disabled={!startDate}
            />
          </div>

          <div className="col-12">
            <p className="text-muted small mb-0">
              Every slot is <b>{SLOT_DURATION_MINUTES / 60} hours</b> long with a max of <b>{DEFAULT_SLOT_CAPACITY} students</b>.
            </p>
            <p className="text-muted small mb-2">
              {weekdaysInRange
                ? "Showing only the weekdays that fall within your selected range."
                : "Hours per weekday — add a second session to leave a break between hours"}
            </p>
          </div>

          {overlapError && (
            <div className="col-12">
              <p className="text-danger small mb-0">{overlapError}</p>
            </div>
          )}
          <div className="col-12">
            <div className="row gx-2">
              {visibleDays.map((day) => (
                <div key={day.weekday} className="col-md-4 mb-2">
                  <div className="border rounded p-3 h-100">

                    <div className="d-flex justify-content-between align-items-center mb-2">
                      <div className="form-check">
                        <input
                          type="checkbox"
                          className="form-check-input"
                          checked={!day.is_closed}
                          onChange={(e) =>
                            updateDay(day.weekday, {
                              is_closed: !e.target.checked,
                              sessions: e.target.checked && day.sessions.length === 0
                                ? [{ start_time: "10:00", end_time: "18:00" }]
                                : day.sessions,
                            })
                          }
                        />
                        <label className="form-check-label fw-medium">{WEEKDAYS[day.weekday]}</label>
                      </div>
                      {day.is_closed && <span className="text-muted small">closed</span>}
                    </div>

                    {!day.is_closed &&
                      day.sessions.map((session, i) => (
                        <div key={i} className="d-flex gap-2 align-items-center mb-2">
                          <input
                            type="time"
                            className="form-control form-control-sm flex-fill"
                            value={session.start_time}
                            onChange={(e) => updateSession(day.weekday, i, { start_time: e.target.value })}
                          />
                          <span className="text-muted small">to</span>
                          <input
                            type="time"
                            className="form-control form-control-sm flex-fill"

                            value={session.end_time}
                            onChange={(e) => updateSession(day.weekday, i, { end_time: e.target.value })}
                          />
                          <button className="btn btn-sm btn-outline-danger" onClick={() => removeSession(day.weekday, i)}>
                            <i className="bi bi-x" />
                          </button>
                        </div>
                      ))}

                    {!day.is_closed && (
                      <button className="btn btn-sm btn-outline-secondary" onClick={() => addSession(day.weekday)}>
                        <i className="ti ti-plus me-1" /> Add session
                      </button>
                    )}
                  </div>

                </div>
              ))}
            </div>
          </div>
          <div className="col-12">
            <div className="alert alert-light border small mb-0">
              {slotsForRange !== null ? (
                <>
                  <b>{slotsForRange}</b> slots will be generated for {startDate} to {endDate}.
                </>
              ) : (
                <>
                  <b>{weeklySlotCount}</b> slots per week with this configuration.
                </>
              )}
            </div>
          </div>

          <div className="col-md-3">
            <button className="admin-theme-btn w-100" onClick={handleSubmit} disabled={loading || !!overlapError}>
              {loading ? "Generating..." : "Generate slots"}
            </button>
          </div>

          {statusMsg && (
            <div className="col-12">
              <p className={`mb-0 small ${statusIsError ? "text-danger" : "text-success"}`}>{statusMsg}</p>
            </div>
          )}
        </div>
      </div>

      <div className="card-theme p-4">
        <div className="row">
          {/* Left Calendar */}
          <div className="col-lg-6">
            <div className="border rounded p-3">
              <div className="d-flex calendar-control justify-content-between align-items-center mb-3">
                <button className="default-btn" onClick={goPrevMonth}>
                  <i className="bi bi-chevron-left" />
                </button>
                <span className="fw-semibold">{monthLabel}</span>
                <button className="default-btn" onClick={goNextMonth}>
                  <i className="bi bi-chevron-right" />
                </button>
              </div>

              <div className="d-grid schedule-slot" style={{ gridTemplateColumns: "repeat(7,1fr)", gap: 4 }}>
                {WEEKDAYS.map((wd) => (
                  <div key={wd} className="text-center small fw-semibold text-muted">
                    {wd.slice(0, 2)}
                  </div>
                ))}

                {calendarCells.map((cell, idx) => {
                  if (!cell.dateKey) return <div key={idx} />;

                  const daySlots = slotsByDate[cell.dateKey] ?? [];
                  const hasSlots = daySlots.length > 0;
                  const isSelected = selectedDate === cell.dateKey;

                  return (
                    <button
                      key={idx}
                      onClick={() => setSelectedDate(hasSlots ? cell.dateKey : null)}
                      className={`schedule-btn ${isSelected ? "schedule-selected" : hasSlots ? "available-schedule" : "not-available"}`}
                    >
                      {cell.dayNumber}
                    </button>
                  );
                })}
              </div>

              <div className="mt-3 d-flex gap-2">
                <button className="schedule-btn default-btn" onClick={goToday}>
                  Today
                </button>
                <button className="schedule-btn admin-theme-btn" onClick={loadAllGeneratedSlots} disabled={calendarLoading}>
                  {calendarLoading ? "Refreshing..." : "Refresh"}
                </button>
              </div>
            </div>
          </div>

          {/* Right Slot Details */}
          <div className="col-lg-6 mt-4 mt-lg-0">
            <div className="border rounded p-3 h-100">
              {!selectedDate ? (
                <div className="text-center text-muted py-5">
                  <i className="ti ti-calendar-event" style={{ fontSize: 50 }} />
                  <p className="mt-3 mb-0">Select a date from the calendar to view slots.</p>
                </div>
              ) : (
                <>
                  <h5 className="mb-3">
                    {new Date(selectedDate + "T00:00:00").toLocaleDateString(undefined, {
                      weekday: "long",
                      month: "long",
                      day: "numeric",
                      year: "numeric",
                    })}
                  </h5>

                  {selectedSlots.length === 0 ? (
                    <p className="text-muted">No slots available.</p>
                  ) : (
                    <div className="row gx-2">
                      {selectedSlots
                        .sort((a, b) => a.start_time.localeCompare(b.start_time))
                        .map((slot) => (
                          <div key={slot.id} className="col-xl-4 col-lg-4 col-md-4 col-sm-4 col-6 mb-2">
                            <div className="timeslotebx h-100">
                              <div className="small fw-medium">
                                {fmt12(slot.start_time)} <span className="small text-muted mb-1">to</span> {fmt12(slot.end_time)}
                              </div>
                              <span className="badge" style={{ fontSize: 11 }}>
                                {slot.booked_count} student{slot.booked_count === 1 ? "" : "s"} assigned
                              </span>
                            </div>
                          </div>
                        ))}
                    </div>
                  )}
                </>
              )}
            </div>
          </div>
        </div>

        {!calendarLoading && Object.keys(slotsByDate).length === 0 && (
          <p className="text-muted small mt-3">No slots created yet. Generate a master above to see them here.</p>
        )}
      </div>
    </div>
  );
}