"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { CalendarClock, ChevronLeft, ChevronRight, Plus } from "lucide-react";
import { useSession } from "next-auth/react";
import { cn } from "@/lib/utils";
import { formatDate, formatDateTime, getStatusPillClass } from "@/lib/marketing/labels";
import type { Employee, Meeting, ProjectCalendarEvent } from "@/lib/projects/types";

type MeetingFilter = "all" | "mine" | "team" | "pending";
type CreateMode = "meeting" | "personal_event";

const SLOT_START_HOUR = 7;
const SLOT_END_HOUR = 21;

function startOfWeekMonday(source: Date): Date {
  const date = new Date(source);
  const day = date.getDay();
  const diff = day === 0 ? -6 : 1 - day;
  date.setDate(date.getDate() + diff);
  date.setHours(0, 0, 0, 0);
  return date;
}

function addDays(base: Date, days: number): Date {
  const next = new Date(base);
  next.setDate(base.getDate() + days);
  return next;
}

function formatDateTimeLocalInput(value: Date): string {
  const year = value.getFullYear();
  const month = String(value.getMonth() + 1).padStart(2, "0");
  const day = String(value.getDate()).padStart(2, "0");
  const hours = String(value.getHours()).padStart(2, "0");
  const minutes = String(value.getMinutes()).padStart(2, "0");
  return `${year}-${month}-${day}T${hours}:${minutes}`;
}

function toIsoOrNull(value: string): string | null {
  if (!value) {
    return null;
  }

  const parsed = new Date(value);
  if (Number.isNaN(parsed.getTime())) {
    return null;
  }

  return parsed.toISOString();
}

function meetingIsMine(meeting: Meeting, userId: string): boolean {
  if (meeting.requestedById === userId) {
    return true;
  }

  return meeting.participants.some((participant) => participant.userId === userId);
}

function meetingHasPendingResponse(meeting: Meeting, userId: string): boolean {
  return meeting.participants.some((participant) => participant.userId === userId && participant.responseStatus === "pending");
}

function canManageProjectsWorkspace(role: string | undefined, department: string | null | undefined): boolean {
  if (role === "owner") {
    return true;
  }

  return (role === "leader" || role === "lead") && department === "proyectos";
}

export default function ProjectsMeetingsPage() {
  const { data: session } = useSession();
  const [weekStart, setWeekStart] = useState<Date>(() => startOfWeekMonday(new Date()));
  const [meetings, setMeetings] = useState<Meeting[]>([]);
  const [events, setEvents] = useState<ProjectCalendarEvent[]>([]);
  const [employees, setEmployees] = useState<Employee[]>([]);
  const [filter, setFilter] = useState<MeetingFilter>("all");
  const [isLoading, setIsLoading] = useState(true);
  const [isSaving, setIsSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const [createMode, setCreateMode] = useState<CreateMode>("meeting");
  const [title, setTitle] = useState("");
  const [agenda, setAgenda] = useState("");
  const [startAtInput, setStartAtInput] = useState(() => {
    const base = addDays(startOfWeekMonday(new Date()), 1);
    base.setHours(9, 0, 0, 0);
    return formatDateTimeLocalInput(base);
  });
  const [endAtInput, setEndAtInput] = useState(() => {
    const base = addDays(startOfWeekMonday(new Date()), 1);
    base.setHours(10, 0, 0, 0);
    return formatDateTimeLocalInput(base);
  });
  const [visibility, setVisibility] = useState<"personal" | "team">("personal");
  const [selectedParticipantIds, setSelectedParticipantIds] = useState<string[]>([]);

  const weekDays = useMemo(() => Array.from({ length: 7 }, (_, dayOffset) => addDays(weekStart, dayOffset)), [weekStart]);
  const weekStartIso = weekDays[0].toISOString();
  const weekEnd = addDays(weekStart, 7);
  const weekEndIso = weekEnd.toISOString();

  const timeSlots = useMemo(() => {
    const slots: Array<{ key: string; label: string }> = [];

    for (let hour = SLOT_START_HOUR; hour < SLOT_END_HOUR; hour += 1) {
      for (const minute of [0, 30]) {
        const key = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
        const label = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
        slots.push({ key, label });
      }
    }

    return slots;
  }, []);

  const loadMeetingsWorkspace = useCallback(async () => {
    setIsLoading(true);
    try {
      const [meetingsResponse, eventsResponse, dashboardResponse] = await Promise.all([
        fetch("/api/projects/meetings", { method: "GET", cache: "no-store" }),
        fetch(`/api/projects/calendar/events?start=${encodeURIComponent(weekStartIso)}&end=${encodeURIComponent(weekEndIso)}`, {
          method: "GET",
          cache: "no-store",
        }),
        fetch("/api/projects/dashboard", { method: "GET", cache: "no-store" }),
      ]);

      const meetingsPayload = (await meetingsResponse.json()) as { error?: string; meetings?: Meeting[] };
      if (!meetingsResponse.ok) {
        throw new Error(meetingsPayload.error ?? "No se pudieron cargar reuniones.");
      }

      const eventsPayload = (await eventsResponse.json()) as { error?: string; events?: ProjectCalendarEvent[] };
      if (!eventsResponse.ok) {
        throw new Error(eventsPayload.error ?? "No se pudieron cargar eventos de calendario.");
      }

      const dashboardPayload = (await dashboardResponse.json()) as { error?: string; employees?: Employee[] };
      if (!dashboardResponse.ok) {
        throw new Error(dashboardPayload.error ?? "No se pudieron cargar participantes.");
      }

      setMeetings(meetingsPayload.meetings ?? []);
      setEvents(eventsPayload.events ?? []);
      setEmployees(dashboardPayload.employees ?? []);
      setError(null);
    } catch (loadError) {
      setError(loadError instanceof Error ? loadError.message : "No se pudo cargar el calendario de reuniones.");
    } finally {
      setIsLoading(false);
    }
  }, [weekEndIso, weekStartIso]);

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

  const userId = session?.user?.id ?? "";
  const canManage = canManageProjectsWorkspace(session?.user?.role, session?.user?.department);

  useEffect(() => {
    if (canManage) {
      return;
    }

    if (createMode === "meeting") {
      setCreateMode("personal_event");
    }

    if (visibility === "team") {
      setVisibility("personal");
    }
  }, [canManage, createMode, visibility]);

  const filteredMeetings = useMemo(() => {
    if (!userId) {
      return meetings;
    }

    if (filter === "mine") {
      return meetings.filter((meeting) => meetingIsMine(meeting, userId));
    }

    if (filter === "pending") {
      return meetings.filter((meeting) => meetingHasPendingResponse(meeting, userId));
    }

    if (filter === "team") {
      return meetings.filter((meeting) => meeting.status !== "cancelled");
    }

    return meetings;
  }, [filter, meetings, userId]);

  const meetingsById = useMemo(() => Object.fromEntries(filteredMeetings.map((meeting) => [meeting.id, meeting])), [filteredMeetings]);

  const calendarItems = useMemo(() => {
    const items = events
      .map((event) => {
        const meeting = event.meetingId ? meetingsById[event.meetingId] : undefined;
        if (event.meetingId && !meeting) {
          return null;
        }

        return {
          id: event.id,
          title: meeting?.title ?? event.title,
          startAt: event.startAt,
          endAt: event.endAt,
          meetingId: event.meetingId,
          status: meeting?.status ?? null,
          visibility: event.visibility,
          mine: event.ownerUserId === userId || Boolean(meeting && meetingIsMine(meeting, userId)),
          pending: Boolean(meeting && meetingHasPendingResponse(meeting, userId)),
        };
      })
      .filter((item): item is NonNullable<typeof item> => Boolean(item));

    return items.filter((item) => {
      if (filter === "mine") {
        return item.mine;
      }

      if (filter === "team") {
        return item.visibility === "team" || Boolean(item.meetingId);
      }

      if (filter === "pending") {
        return item.pending;
      }

      return true;
    });
  }, [events, filter, meetingsById, userId]);

  const slotAssignments = useMemo(() => {
    const map = new Map<string, typeof calendarItems>();

    for (const item of calendarItems) {
      const date = new Date(item.startAt);
      const dayKey = date.toISOString().slice(0, 10);
      const slotKey = `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
      const key = `${dayKey}_${slotKey}`;
      const current = map.get(key) ?? [];
      current.push(item);
      map.set(key, current);
    }

    return map;
  }, [calendarItems]);

  const weekTitle = `${formatDate(weekDays[0].toISOString())} - ${formatDate(weekDays[6].toISOString())}`;

  const handleCreate = async () => {
    const startAt = toIsoOrNull(startAtInput);
    const endAt = toIsoOrNull(endAtInput);

    if (!title.trim() || !startAt || !endAt) {
      setError("Completa título, inicio y fin.");
      return;
    }

    if (new Date(endAt).getTime() <= new Date(startAt).getTime()) {
      setError("La hora de fin debe ser posterior al inicio.");
      return;
    }

    setIsSaving(true);

    try {
      if (createMode === "meeting") {
        if (!canManage) {
          throw new Error("Solo owners o líderes pueden crear reuniones de equipo.");
        }

        const response = await fetch("/api/projects/meetings", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            kind: "schedule",
            title,
            agenda,
            startAt,
            endAt,
            participants: selectedParticipantIds.map((participantId) => ({ userId: participantId })),
          }),
        });

        const payload = (await response.json()) as { error?: string };
        if (!response.ok) {
          throw new Error(payload.error ?? "No se pudo crear la reunión.");
        }
      } else {
        const response = await fetch("/api/projects/calendar/events", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            title,
            notes: agenda,
            startAt,
            endAt,
            visibility: canManage ? visibility : "personal",
          }),
        });

        const payload = (await response.json()) as { error?: string };
        if (!response.ok) {
          throw new Error(payload.error ?? "No se pudo crear el evento personal.");
        }
      }

      setTitle("");
      setAgenda("");
      setSelectedParticipantIds([]);
      setError(null);
      await loadMeetingsWorkspace();
    } catch (createError) {
      setError(createError instanceof Error ? createError.message : "No se pudo guardar el evento.");
    } finally {
      setIsSaving(false);
    }
  };

  const handleRespond = async (meetingId: string, responseStatus: "accepted" | "declined") => {
    setIsSaving(true);

    try {
      const response = await fetch(`/api/projects/meetings/${meetingId}/respond`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ responseStatus }),
      });

      const payload = (await response.json()) as { error?: string };
      if (!response.ok) {
        throw new Error(payload.error ?? "No se pudo responder la invitación.");
      }

      await loadMeetingsWorkspace();
    } catch (respondError) {
      setError(respondError instanceof Error ? respondError.message : "No se pudo responder la invitación.");
    } finally {
      setIsSaving(false);
    }
  };

  return (
    <div className="space-y-5">
      <section className="rounded-benell border border-benell-stroke bg-benell-surface p-5 shadow-benell">
        <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
          <div>
            <p className="text-sm font-semibold uppercase tracking-wide text-benell-brown">Projects Meetings</p>
            <h1 className="mt-1 text-2xl font-semibold text-benell-text">Calendario semanal</h1>
            <p className="mt-1 text-sm text-benell-text-soft">Planeación de reuniones y eventos personales/equipo.</p>
          </div>

          <div className="flex flex-wrap items-center gap-2">
            <button
              type="button"
              className="inline-flex items-center gap-1 rounded-lg border border-benell-stroke px-3 py-1.5 text-sm font-semibold text-benell-text"
              onClick={() => setWeekStart((prev) => addDays(prev, -7))}
            >
              <ChevronLeft className="size-4" aria-hidden /> Semana anterior
            </button>
            <span className="rounded-lg border border-benell-stroke bg-white px-3 py-1.5 text-sm text-benell-text">{weekTitle}</span>
            <button
              type="button"
              className="inline-flex items-center gap-1 rounded-lg border border-benell-stroke px-3 py-1.5 text-sm font-semibold text-benell-text"
              onClick={() => setWeekStart((prev) => addDays(prev, 7))}
            >
              Semana siguiente <ChevronRight className="size-4" aria-hidden />
            </button>
          </div>
        </div>
      </section>

      <section className="rounded-benell border border-benell-stroke bg-benell-surface p-5 shadow-benell">
        <div className="flex flex-wrap items-center gap-2">
          {([
            ["all", "All"],
            ["mine", "Mine"],
            ["team", "Team"],
            ["pending", "Pending response"],
          ] as Array<[MeetingFilter, string]>).map(([key, label]) => (
            <button
              key={key}
              type="button"
              onClick={() => setFilter(key)}
              className={cn(
                "rounded-full border px-3 py-1 text-xs font-semibold uppercase tracking-wide",
                filter === key ? "border-benell-brown bg-benell-brown text-white" : "border-benell-stroke bg-white text-benell-text"
              )}
            >
              {label}
            </button>
          ))}
        </div>
      </section>

      {error ? <section className="rounded-benell border border-benell-red/40 bg-benell-red/10 p-4 text-sm text-benell-red">{error}</section> : null}

      <section className="rounded-benell border border-benell-stroke bg-benell-surface p-5 shadow-benell">
        <header className="mb-4 flex items-center gap-2">
          <CalendarClock className="size-4 text-benell-brown" aria-hidden />
          <h2 className="text-base font-semibold text-benell-text">Semana (07:00 - 21:00, intervalos de 30 min)</h2>
        </header>

        <div className="overflow-x-auto">
          <div className="grid min-w-[900px] grid-cols-[84px_repeat(7,minmax(100px,1fr))] border border-benell-stroke">
            <div className="border-b border-r border-benell-stroke bg-benell-surface-muted px-2 py-2 text-xs font-semibold uppercase text-benell-text-soft">
              Hora
            </div>
            {weekDays.map((day) => (
              <div key={day.toISOString()} className="border-b border-r border-benell-stroke bg-benell-surface-muted px-2 py-2 text-xs font-semibold uppercase text-benell-text-soft">
                {new Intl.DateTimeFormat("es-MX", { weekday: "short", day: "2-digit", month: "short" }).format(day)}
              </div>
            ))}

            {timeSlots.map((slot) => (
              <div key={`row_${slot.key}`} className="contents">
                <div className="border-r border-t border-benell-stroke bg-white px-2 py-2 text-xs text-benell-text-soft">
                  {slot.label}
                </div>
                {weekDays.map((day) => {
                  const dayKey = day.toISOString().slice(0, 10);
                  const key = `${dayKey}_${slot.key}`;
                  const slotItems = slotAssignments.get(key) ?? [];

                  return (
                    <div key={key} className="min-h-[52px] border-r border-t border-benell-stroke bg-white p-1">
                      {slotItems.map((item) => (
                        <div
                          key={item.id}
                          className={cn(
                            "mb-1 rounded-md border px-2 py-1 text-[11px]",
                            item.meetingId ? "border-benell-brown/40 bg-benell-brown/10 text-benell-brown" : "border-benell-stroke bg-benell-surface-muted text-benell-text"
                          )}
                        >
                          <p className="line-clamp-2 font-semibold">{item.title}</p>
                          <p className="text-[10px]">{new Date(item.startAt).toLocaleTimeString("es-MX", { hour: "2-digit", minute: "2-digit" })}</p>
                        </div>
                      ))}
                    </div>
                  );
                })}
              </div>
            ))}
          </div>
        </div>
      </section>

      <section className="grid gap-5 xl:grid-cols-2">
        <article className="rounded-benell border border-benell-stroke bg-benell-surface p-5 shadow-benell">
          <header className="mb-3 flex items-center gap-2">
            <Plus className="size-4 text-benell-brown" aria-hidden />
            <h2 className="text-base font-semibold text-benell-text">Crear evento</h2>
          </header>

          <div className="space-y-3">
            <label className="flex flex-col gap-1 text-sm text-benell-text-soft">
              Tipo
              <select className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm text-benell-text" value={createMode} onChange={(event) => setCreateMode(event.target.value as CreateMode)}>
                {canManage ? <option value="meeting">meeting</option> : null}
                <option value="personal_event">personal_event</option>
              </select>
              {!canManage ? <span className="text-xs text-benell-text-soft">Como colaborador, solo puedes crear eventos personales.</span> : null}
            </label>

            <label className="flex flex-col gap-1 text-sm text-benell-text-soft">
              Título
              <input
                value={title}
                onChange={(event) => setTitle(event.target.value)}
                className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm text-benell-text"
                placeholder="Planeación semanal"
              />
            </label>

            <label className="flex flex-col gap-1 text-sm text-benell-text-soft">
              Agenda / notas
              <textarea
                value={agenda}
                onChange={(event) => setAgenda(event.target.value)}
                className="min-h-[88px] rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm text-benell-text"
                placeholder="Objetivos y puntos clave"
              />
            </label>

            <div className="grid gap-3 sm:grid-cols-2">
              <label className="flex flex-col gap-1 text-sm text-benell-text-soft">
                Inicio
                <input
                  type="datetime-local"
                  value={startAtInput}
                  onChange={(event) => setStartAtInput(event.target.value)}
                  className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm text-benell-text"
                />
              </label>

              <label className="flex flex-col gap-1 text-sm text-benell-text-soft">
                Fin
                <input
                  type="datetime-local"
                  value={endAtInput}
                  onChange={(event) => setEndAtInput(event.target.value)}
                  className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm text-benell-text"
                />
              </label>
            </div>

            {createMode === "meeting" ? (
              <label className="flex flex-col gap-1 text-sm text-benell-text-soft">
                Participantes
                <select
                  multiple
                  value={selectedParticipantIds}
                  onChange={(event) => {
                    const values = Array.from(event.target.selectedOptions).map((option) => option.value);
                    setSelectedParticipantIds(values);
                  }}
                  className="min-h-[110px] rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm text-benell-text"
                >
                  {employees.map((employee) => (
                    <option key={employee.id} value={employee.id}>
                      {employee.name}
                    </option>
                  ))}
                </select>
              </label>
            ) : (
              <label className="flex flex-col gap-1 text-sm text-benell-text-soft">
                Visibilidad
                <select
                  value={visibility}
                  onChange={(event) => setVisibility(event.target.value as "personal" | "team")}
                  className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm text-benell-text"
                >
                  <option value="personal">personal</option>
                  {canManage ? <option value="team">team</option> : null}
                </select>
              </label>
            )}

            <button
              type="button"
              onClick={() => void handleCreate()}
              disabled={isSaving}
              className="inline-flex items-center justify-center rounded-lg bg-benell-brown px-4 py-2 text-sm font-semibold text-white transition disabled:cursor-not-allowed disabled:opacity-70"
            >
              {isSaving ? "Guardando..." : "Crear"}
            </button>
          </div>
        </article>

        <article className="rounded-benell border border-benell-stroke bg-benell-surface p-5 shadow-benell">
          <h2 className="text-base font-semibold text-benell-text">Reuniones y confirmaciones</h2>
          <p className="mt-1 text-sm text-benell-text-soft">Estado de asistencia y pendientes de confirmación.</p>

          <div className="mt-3 space-y-2">
            {filteredMeetings.slice(0, 14).map((meeting) => {
              const myParticipant = meeting.participants.find((participant) => participant.userId === userId) ?? null;
              const pendingCount = meeting.participantCounts.pending;

              return (
                <article key={meeting.id} className="rounded-lg border border-benell-stroke bg-white px-3 py-2">
                  <div className="flex flex-wrap items-start justify-between gap-2">
                    <div>
                      <p className="text-sm font-semibold text-benell-text">{meeting.title}</p>
                      <p className="text-xs text-benell-text-soft">{formatDateTime(meeting.scheduledFor)}</p>
                    </div>
                    <span className={cn("rounded-full px-2 py-0.5 text-xs font-semibold", getStatusPillClass(meeting.status))}>{meeting.status}</span>
                  </div>

                  <div className="mt-2 flex flex-wrap items-center gap-2 text-xs text-benell-text-soft">
                    <span>Confirmados: {meeting.participantCounts.accepted}</span>
                    <span>Pendientes: {pendingCount}</span>
                    <span>Declinados: {meeting.participantCounts.declined}</span>
                  </div>

                  {myParticipant ? (
                    <div className="mt-2 flex gap-2">
                      <button
                        type="button"
                        disabled={isSaving || myParticipant.responseStatus === "accepted"}
                        onClick={() => void handleRespond(meeting.id, "accepted")}
                        className="rounded-md border border-benell-green/40 bg-benell-green/10 px-2 py-1 text-xs font-semibold text-benell-green disabled:opacity-60"
                      >
                        Accept
                      </button>
                      <button
                        type="button"
                        disabled={isSaving || myParticipant.responseStatus === "declined"}
                        onClick={() => void handleRespond(meeting.id, "declined")}
                        className="rounded-md border border-benell-red/40 bg-benell-red/10 px-2 py-1 text-xs font-semibold text-benell-red disabled:opacity-60"
                      >
                        Decline
                      </button>
                    </div>
                  ) : null}
                </article>
              );
            })}

            {filteredMeetings.length === 0 ? <p className="text-sm text-benell-text-soft">Sin reuniones para el filtro actual.</p> : null}
          </div>
        </article>
      </section>

      {isLoading ? <section className="rounded-benell border border-benell-stroke bg-benell-surface p-4 text-sm text-benell-text-soft">Actualizando calendario...</section> : null}
    </div>
  );
}
