"use client";

import { Button } from "@mdxui/primitives";
import { cn } from "@mdxui/primitives/lib/utils";
import {
	addDays,
	addMonths,
	addWeeks,
	endOfMonth,
	endOfWeek,
	format,
	isSameDay,
	isSameMonth,
	startOfDay,
	startOfMonth,
	startOfWeek,
} from "date-fns";
import { ChevronLeft, ChevronRight } from "lucide-react";
import * as React from "react";
import type { CalendarProps, CalendarView } from "./types";

export function Calendar({
	view = "month",
	date = new Date(),
	events = [],
	onViewChange,
	onDateChange,
	onEventClick,
	onSlotClick,
	className,
}: CalendarProps) {
	const [currentDate, setCurrentDate] = React.useState(date);
	const [currentView, setCurrentView] = React.useState<CalendarView>(view);

	React.useEffect(() => {
		setCurrentDate(date);
	}, [date]);

	React.useEffect(() => {
		setCurrentView(view);
	}, [view]);

	const handlePrevious = () => {
		const newDate =
			currentView === "month"
				? addMonths(currentDate, -1)
				: currentView === "week"
					? addWeeks(currentDate, -1)
					: addDays(currentDate, -1);
		setCurrentDate(newDate);
		onDateChange?.(newDate);
	};

	const handleNext = () => {
		const newDate =
			currentView === "month"
				? addMonths(currentDate, 1)
				: currentView === "week"
					? addWeeks(currentDate, 1)
					: addDays(currentDate, 1);
		setCurrentDate(newDate);
		onDateChange?.(newDate);
	};

	const handleToday = () => {
		const today = new Date();
		setCurrentDate(today);
		onDateChange?.(today);
	};

	const handleViewChange = (newView: CalendarView) => {
		setCurrentView(newView);
		onViewChange?.(newView);
	};

	const getEventsForDay = (day: Date) => {
		return events.filter((event) => {
			const eventStart = startOfDay(event.start);
			const eventEnd = startOfDay(event.end);
			const checkDay = startOfDay(day);
			return checkDay >= eventStart && checkDay <= eventEnd;
		});
	};

	const renderMonthView = () => {
		const monthStart = startOfMonth(currentDate);
		const monthEnd = endOfMonth(currentDate);
		const startDate = startOfWeek(monthStart);
		const endDate = endOfWeek(monthEnd);

		const rows = [];
		let days = [];
		let day = startDate;

		while (day <= endDate) {
			for (let i = 0; i < 7; i++) {
				const dayEvents = getEventsForDay(day);
				const isCurrentMonth = isSameMonth(day, monthStart);
				const isToday = isSameDay(day, new Date());
				const dayKey = day.toString();
				const currentDay = day;

				days.push(
					<button
						key={dayKey}
						type="button"
						onClick={() => onSlotClick?.(currentDay)}
						className={cn(
							"relative min-h-[100px] border-r border-b p-2 text-left transition-colors hover:bg-accent/50",
							!isCurrentMonth && "bg-muted/20 text-muted-foreground",
							isToday && "bg-accent/30",
						)}
					>
						<div
							className={cn(
								"mb-1 flex size-6 items-center justify-center rounded-full text-sm font-medium",
								isToday && "bg-primary text-primary-foreground",
							)}
						>
							{format(day, "d")}
						</div>
						<div className="space-y-1">
							{dayEvents.slice(0, 3).map((event) => (
								<button
									key={event.id}
									type="button"
									onClick={(e) => {
										e.stopPropagation();
										onEventClick?.(event);
									}}
									className={cn(
										"w-full truncate rounded px-1 py-0.5 text-xs hover:opacity-80",
										event.color || "bg-primary/80 text-primary-foreground",
									)}
									style={
										event.color ? { backgroundColor: event.color } : undefined
									}
								>
									{event.title}
								</button>
							))}
							{dayEvents.length > 3 && (
								<div className="text-xs text-muted-foreground">
									+{dayEvents.length - 3} more
								</div>
							)}
						</div>
					</button>,
				);
				day = addDays(day, 1);
			}
			rows.push(
				<div key={day.toString()} className="grid grid-cols-7">
					{days}
				</div>,
			);
			days = [];
		}

		return (
			<div>
				<div className="grid grid-cols-7 border-b border-l border-t">
					{["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((day) => (
						<div
							key={day}
							className="border-r p-2 text-center text-sm font-semibold"
						>
							{day}
						</div>
					))}
				</div>
				<div className="border-l">{rows}</div>
			</div>
		);
	};

	const renderWeekView = () => {
		const weekStart = startOfWeek(currentDate);
		const days = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i));
		const hours = Array.from({ length: 24 }, (_, i) => i);

		return (
			<div className="flex flex-col">
				<div className="grid grid-cols-8 border-b border-l border-t">
					<div className="border-r p-2 text-sm font-semibold" />
					{days.map((day) => (
						<div key={day.toString()} className="border-r p-2 text-center">
							<div className="text-sm font-semibold">{format(day, "EEE")}</div>
							<div
								className={cn(
									"mx-auto mt-1 flex size-8 items-center justify-center rounded-full text-sm",
									isSameDay(day, new Date()) &&
										"bg-primary text-primary-foreground",
								)}
							>
								{format(day, "d")}
							</div>
						</div>
					))}
				</div>
				<div className="grid grid-cols-8 border-l">
					{hours.map((hour) => (
						<React.Fragment key={hour}>
							<div className="border-b border-r p-2 text-xs text-muted-foreground">
								{format(new Date().setHours(hour, 0), "ha")}
							</div>
							{days.map((day) => {
								const slotDate = new Date(day);
								slotDate.setHours(hour, 0, 0, 0);
								return (
									<button
										key={`${day.toString()}-${hour}`}
										type="button"
										onClick={() => onSlotClick?.(slotDate)}
										className="min-h-[60px] border-b border-r p-1 transition-colors hover:bg-accent/50"
									/>
								);
							})}
						</React.Fragment>
					))}
				</div>
			</div>
		);
	};

	const renderDayView = () => {
		const hours = Array.from({ length: 24 }, (_, i) => i);
		const dayEvents = getEventsForDay(currentDate);

		return (
			<div className="flex flex-col">
				<div className="border-b border-l border-t p-4 text-center">
					<div className="text-sm font-semibold">
						{format(currentDate, "EEEE")}
					</div>
					<div
						className={cn(
							"mx-auto mt-2 flex size-12 items-center justify-center rounded-full text-lg",
							isSameDay(currentDate, new Date()) &&
								"bg-primary text-primary-foreground",
						)}
					>
						{format(currentDate, "d")}
					</div>
				</div>
				<div className="grid grid-cols-[auto_1fr] border-l">
					{hours.map((hour) => {
						const slotDate = new Date(currentDate);
						slotDate.setHours(hour, 0, 0, 0);

						return (
							<React.Fragment key={hour}>
								<div className="border-b border-r p-2 text-xs text-muted-foreground">
									{format(new Date().setHours(hour, 0), "ha")}
								</div>
								<button
									type="button"
									onClick={() => onSlotClick?.(slotDate)}
									className="min-h-[80px] border-b border-r p-2 transition-colors hover:bg-accent/50"
								>
									{dayEvents
										.filter((event) => event.start.getHours() === hour)
										.map((event) => (
											<button
												key={event.id}
												type="button"
												onClick={(e) => {
													e.stopPropagation();
													onEventClick?.(event);
												}}
												className={cn(
													"mb-1 w-full truncate rounded px-2 py-1 text-left text-sm hover:opacity-80",
													event.color ||
														"bg-primary/80 text-primary-foreground",
												)}
												style={
													event.color
														? { backgroundColor: event.color }
														: undefined
												}
											>
												{event.title}
											</button>
										))}
								</button>
							</React.Fragment>
						);
					})}
				</div>
			</div>
		);
	};

	return (
		<div className={cn("flex flex-col gap-4", className)}>
			<div className="flex items-center justify-between">
				<div className="flex items-center gap-2">
					<Button variant="outline" size="icon-sm" onClick={handlePrevious}>
						<ChevronLeft className="size-4" />
					</Button>
					<Button variant="outline" size="sm" onClick={handleToday}>
						Today
					</Button>
					<Button variant="outline" size="icon-sm" onClick={handleNext}>
						<ChevronRight className="size-4" />
					</Button>
					<h2 className="ml-4 text-lg font-semibold">
						{currentView === "month" && format(currentDate, "MMMM yyyy")}
						{currentView === "week" &&
							`Week of ${format(startOfWeek(currentDate), "MMM d, yyyy")}`}
						{currentView === "day" && format(currentDate, "MMMM d, yyyy")}
					</h2>
				</div>
				<div className="flex items-center gap-2">
					<Button
						variant={currentView === "month" ? "default" : "outline"}
						size="sm"
						onClick={() => handleViewChange("month")}
					>
						Month
					</Button>
					<Button
						variant={currentView === "week" ? "default" : "outline"}
						size="sm"
						onClick={() => handleViewChange("week")}
					>
						Week
					</Button>
					<Button
						variant={currentView === "day" ? "default" : "outline"}
						size="sm"
						onClick={() => handleViewChange("day")}
					>
						Day
					</Button>
				</div>
			</div>
			<div className="overflow-auto rounded-md border bg-card">
				{currentView === "month" && renderMonthView()}
				{currentView === "week" && renderWeekView()}
				{currentView === "day" && renderDayView()}
			</div>
		</div>
	);
}
