import { Head, router } from '@inertiajs/react';
import {
    Clock,
    MapPin,
    Pencil,
    Plus,
    Trash2,
    User,
    Video,
} from 'lucide-react';
import { useState } from 'react';
import { DeleteConfirmDialog } from '@/components/delete-confirm-dialog';
import type { FilterField } from '@/components/filter-dialog';
import { FilterDialog } from '@/components/filter-dialog';
import { FormDialog } from '@/components/form-dialog';
import InputError from '@/components/input-error';
import { PageHeader } from '@/components/page-header';
import { RowActions } from '@/components/row-actions';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
    Combobox,
    ComboboxCollection,
    ComboboxContent,
    ComboboxEmpty,
    ComboboxGroup,
    ComboboxInput,
    ComboboxItem,
    ComboboxLabel,
    ComboboxList,
} from '@/components/ui/combobox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from '@/components/ui/select';
import { usePermissions } from '@/hooks/use-permissions';
import { useServerTable } from '@/hooks/use-server-table';
import { cn } from '@/lib/utils';
import {
    destroy,
    index as scheduleIndex,
    store,
    update,
} from '@/routes/admin/academic-classes/schedules';
import { formatAcademicTermLabel } from '@/types/academic-term';
import { ClassMethodLabels } from '@/types/course-class';
import type { ClassMethod } from '@/types/course-class';
import type { Schedule } from '@/types/schedule';
import { DayOfWeekLabels, DaysOfWeek } from '@/types/schedule';

type CourseClassOption = {
    id: number;
    course: {
        id: number;
        code: string;
        name: string;
        semester_number: number | null;
        department: { id: number; name: string } | null;
    } | null;
    lecturer: {
        id: number;
        user: { profile: { full_name: string } | null } | null;
    } | null;
    academic_term: {
        id: number;
        academic_year: string;
        semester: string;
    } | null;
};

type CourseClassGroup = { value: string; items: CourseClassOption[] };

type AcademicTermOption = {
    id: number;
    academic_year: string;
    semester: string;
};

type DepartmentOption = {
    id: number;
    name: string;
};

type Props = {
    schedules: Schedule[];
    courseClasses: CourseClassOption[];
    academicTerms: AcademicTermOption[];
    departments: DepartmentOption[];
    semesterNumbers: number[];
    isPersonalView: boolean;
    highlight?: number;
    filters: {
        academic_term_id?: string;
        department_id?: string;
        semester_number?: string;
    };
};

const UNSCHEDULED = '__unscheduled__';

const BOARD_COLUMNS = [...DaysOfWeek, UNSCHEDULED] as const;

const DEPARTMENT_PALETTE = [
    { border: 'border-l-blue-500 bg-blue-50/60 dark:bg-blue-950/20', dot: 'bg-blue-500' },
    { border: 'border-l-emerald-500 bg-emerald-50/60 dark:bg-emerald-950/20', dot: 'bg-emerald-500' },
    { border: 'border-l-amber-500 bg-amber-50/60 dark:bg-amber-950/20', dot: 'bg-amber-500' },
    { border: 'border-l-violet-500 bg-violet-50/60 dark:bg-violet-950/20', dot: 'bg-violet-500' },
    { border: 'border-l-rose-500 bg-rose-50/60 dark:bg-rose-950/20', dot: 'bg-rose-500' },
    { border: 'border-l-cyan-500 bg-cyan-50/60 dark:bg-cyan-950/20', dot: 'bg-cyan-500' },
    { border: 'border-l-orange-500 bg-orange-50/60 dark:bg-orange-950/20', dot: 'bg-orange-500' },
    { border: 'border-l-fuchsia-500 bg-fuchsia-50/60 dark:bg-fuchsia-950/20', dot: 'bg-fuchsia-500' },
] as const;

function departmentPalette(departmentId: number | undefined) {
    if (!departmentId) {
        return { border: 'border-l-border', dot: 'bg-muted-foreground/40' };
    }

    return DEPARTMENT_PALETTE[departmentId % DEPARTMENT_PALETTE.length];
}

function courseClassLabel(courseClass: {
    course: { code: string; name: string } | null;
} | null): string {
    return `${courseClass?.course?.code ?? ''} ${courseClass?.course?.name ?? ''}`;
}

function courseClassOptionLabel(option: CourseClassOption): string {
    return `${option.course?.code ?? ''} - ${option.course?.name ?? ''}`;
}

function groupCourseClassesByDepartment(
    options: CourseClassOption[],
): CourseClassGroup[] {
    const groups: CourseClassGroup[] = [];
    let currentKey: string | null = null;

    for (const option of options) {
        const key = `${option.course?.department?.name ?? 'Tanpa Jurusan'} — Semester ${option.course?.semester_number ?? 'Tidak ditentukan'}`;

        if (key !== currentKey) {
            currentKey = key;
            groups.push({ value: key, items: [] });
        }

        groups[groups.length - 1].items.push(option);
    }

    return groups;
}

function toTimeInput(value: string | null): string {
    return value ? value.slice(0, 5) : '';
}

export default function ScheduleIndex({
    schedules,
    courseClasses,
    academicTerms,
    departments,
    semesterNumbers,
    isPersonalView,
    highlight,
    filters,
}: Props) {
    const [createOpen, setCreateOpen] = useState(false);
    const [editing, setEditing] = useState<Schedule | null>(null);
    const [deleting, setDeleting] = useState<Schedule | null>(null);
    const { hasPermission } = usePermissions();
    const canCreate = hasPermission('create-schedules');
    const canUpdate = hasPermission('update-schedules');
    const canDelete = hasPermission('delete-schedules');

    const filterFields: FilterField[] = [
        {
            key: 'academic_term_id',
            label: 'Periode Akademik',
            options: academicTerms.map((term) => ({
                value: String(term.id),
                label: formatAcademicTermLabel(term),
            })),
        },
        ...(isPersonalView
            ? []
            : [
                  {
                      key: 'department_id',
                      label: 'Jurusan',
                      options: departments.map((department) => ({
                          value: String(department.id),
                          label: department.name,
                      })),
                  },
                  {
                      key: 'semester_number',
                      label: 'Semester',
                      options: semesterNumbers.map((semester) => ({
                          value: String(semester),
                          label: String(semester),
                      })),
                  },
              ]),
    ];

    const { applyFilters } = useServerTable({
        route: () => scheduleIndex.url(),
        pagination: {
            current_page: 1,
            last_page: 1,
            per_page: 999999,
            total: schedules.length,
        },
        filters,
    });

    function handleApplyFilters(newFilters: Record<string, string>) {
        // Without an explicit `academic_term_id`, the backend defaults it back
        // to the active term, so clearing it needs to be sent explicitly
        // instead of just omitting the key.
        const clearedAcademicTerm =
            Boolean(filters.academic_term_id) && !newFilters.academic_term_id;

        applyFilters({
            ...newFilters,
            ...(clearedAcademicTerm ? { academic_term_id: '' } : {}),
        });
    }

    function handleDelete() {
        if (!deleting) {
            return;
        }

        router.delete(destroy(deleting.id), {
            onSuccess: () => setDeleting(null),
        });
    }

    const grouped = new Map<string, Schedule[]>();

    for (const schedule of schedules) {
        const key = schedule.day_of_week ?? UNSCHEDULED;
        grouped.set(key, [...(grouped.get(key) ?? []), schedule]);
    }

    const columns = BOARD_COLUMNS.filter(
        (day) =>
            day !== UNSCHEDULED || (grouped.get(UNSCHEDULED)?.length ?? 0) > 0,
    );

    const departmentsInView = Array.from(
        new Map(
            schedules
                .map((schedule) => schedule.course_class?.course?.department)
                .filter((department): department is { id: number; name: string } => !!department)
                .map((department) => [department.id, department]),
        ).values(),
    );

    return (
        <>
            <Head title="Jadwal" />

            <div className="flex h-full flex-1 flex-col gap-6 p-4 md:p-6">
                <PageHeader
                    title="Jadwal"
                    actions={
                        <div className="flex items-center gap-2">
                            <FilterDialog
                                fields={filterFields}
                                activeFilters={filters}
                                onApply={handleApplyFilters}
                            />
                            {canCreate && (
                                <Button asChild>
                                    <button
                                        type="button"
                                        onClick={() => setCreateOpen(true)}
                                    >
                                        <Plus className="h-4 w-4" />
                                        Tambah
                                    </button>
                                </Button>
                            )}
                        </div>
                    }
                />

                <div className="flex flex-wrap items-center justify-between gap-3">
                    <p className="hidden text-xs text-muted-foreground md:block">
                        Geser ke samping untuk melihat hari lainnya.
                    </p>

                    {departmentsInView.length > 1 && (
                        <div className="flex flex-wrap items-center gap-3 text-xs text-muted-foreground">
                            {departmentsInView.map((department) => (
                                <span
                                    key={department.id}
                                    className="inline-flex items-center gap-1.5"
                                >
                                    <span
                                        className={cn(
                                            'h-2.5 w-2.5 rounded-full',
                                            departmentPalette(department.id)
                                                .dot,
                                        )}
                                    />
                                    {department.name}
                                </span>
                            ))}
                        </div>
                    )}
                </div>

                <CreateForm
                    open={createOpen}
                    onOpenChange={setCreateOpen}
                    courseClasses={courseClasses}
                />

                <EditForm
                    key={editing?.id}
                    open={editing !== null}
                    onOpenChange={(open) => {
                        if (!open) {
                            setEditing(null);
                        }
                    }}
                    editing={editing}
                    courseClasses={courseClasses}
                />

                {schedules.length === 0 ? (
                    <p className="text-sm text-muted-foreground">
                        Belum ada data jadwal.
                    </p>
                ) : (
                    <div className="flex flex-1 flex-col gap-4 pb-2 md:flex-row md:overflow-x-auto">
                        {columns.map((day) => {
                            const items = grouped.get(day) ?? [];

                            return (
                                <div
                                    key={day}
                                    className="flex w-full shrink-0 flex-col gap-3 border-b pb-6 last:border-b-0 last:pb-0 md:w-72 md:border-b-0 md:pb-0"
                                >
                                    <div className="flex items-center justify-between rounded-md bg-muted px-3 py-2">
                                        <span className="text-sm font-semibold">
                                            {day === UNSCHEDULED
                                                ? 'Belum Diatur'
                                                : DayOfWeekLabels[day]}
                                        </span>
                                        <Badge variant="secondary">
                                            {items.length}
                                        </Badge>
                                    </div>

                                    <div className="flex flex-col gap-3">
                                        {items.length === 0 ? (
                                            <p className="rounded-md border border-dashed p-4 text-center text-xs text-muted-foreground">
                                                Tidak ada jadwal
                                            </p>
                                        ) : (
                                            items.map((schedule) => (
                                                <Card
                                                    key={schedule.id}
                                                    className={cn(
                                                        'gap-3 border-l-4 py-4',
                                                        departmentPalette(
                                                            schedule
                                                                .course_class
                                                                ?.course
                                                                ?.department
                                                                ?.id,
                                                        ).border,
                                                        highlight ===
                                                        schedule.id &&
                                                        'ring-2 ring-primary',
                                                    )}
                                                >
                                                    <CardHeader className="flex flex-row items-start justify-between gap-2 px-4">
                                                        <CardTitle className="text-sm leading-tight">
                                                            {courseClassLabel(
                                                                schedule.course_class ?? {
                                                                    course: null,
                                                                },
                                                            )}
                                                        </CardTitle>
                                                        <RowActions
                                                            wrapperClassName="-mt-1 -mr-2 flex items-center gap-1"
                                                            actions={[
                                                                {
                                                                    label: 'Edit',
                                                                    icon: (
                                                                        <Pencil className="h-3.5 w-3.5" />
                                                                    ),
                                                                    show: canUpdate,
                                                                    onClick:
                                                                        () =>
                                                                            setEditing(
                                                                                schedule,
                                                                            ),
                                                                },
                                                                {
                                                                    label: 'Hapus',
                                                                    icon: (
                                                                        <Trash2 className="h-3.5 w-3.5 text-destructive" />
                                                                    ),
                                                                    show: canDelete,
                                                                    onClick:
                                                                        () =>
                                                                            setDeleting(
                                                                                schedule,
                                                                            ),
                                                                },
                                                            ]}
                                                        />
                                                    </CardHeader>
                                                    <CardContent className="flex flex-col gap-1.5 px-4 text-xs text-muted-foreground">
                                                        {(schedule.start_time ||
                                                            schedule.end_time) && (
                                                                <span className="inline-flex items-center gap-1.5">
                                                                    <Clock className="h-3.5 w-3.5 shrink-0" />
                                                                    {toTimeInput(
                                                                        schedule.start_time,
                                                                    ) || '-'}{' '}
                                                                    -{' '}
                                                                    {toTimeInput(
                                                                        schedule.end_time,
                                                                    ) || '-'}
                                                                </span>
                                                            )}
                                                        {schedule.course_class
                                                            ?.lecturer && (
                                                            <span className="inline-flex items-center gap-1.5">
                                                                <User className="h-3.5 w-3.5 shrink-0" />
                                                                {schedule
                                                                    .course_class
                                                                    .lecturer
                                                                    .user
                                                                    ?.profile
                                                                    ?.full_name ??
                                                                    'N/A'}
                                                            </span>
                                                        )}
                                                        {schedule.room && (
                                                            <span className="inline-flex items-center gap-1.5">
                                                                <MapPin className="h-3.5 w-3.5 shrink-0" />
                                                                {schedule.room}
                                                            </span>
                                                        )}
                                                        {schedule.online_link && (
                                                            <a
                                                                href={
                                                                    schedule.online_link
                                                                }
                                                                target="_blank"
                                                                rel="noreferrer"
                                                                className="inline-flex items-center gap-1.5 text-primary underline underline-offset-4 hover:text-primary/80"
                                                            >
                                                                <Video className="h-3.5 w-3.5 shrink-0" />
                                                                Link Online
                                                            </a>
                                                        )}
                                                        {schedule.course_class
                                                            ?.method && (
                                                            <Badge
                                                                variant="outline"
                                                                className="w-fit text-[10px] font-normal"
                                                            >
                                                                {ClassMethodLabels[
                                                                    schedule
                                                                        .course_class
                                                                        .method as ClassMethod
                                                                ] ??
                                                                    schedule
                                                                        .course_class
                                                                        .method}
                                                            </Badge>
                                                        )}
                                                    </CardContent>
                                                </Card>
                                            ))
                                        )}
                                    </div>
                                </div>
                            );
                        })}
                    </div>
                )}

                <DeleteConfirmDialog
                    target={deleting}
                    onOpenChange={(open) => {
                        if (!open) {
                            setDeleting(null);
                        }
                    }}
                    title="Hapus Jadwal"
                    description={(schedule) =>
                        `Apakah Anda yakin ingin menghapus jadwal "${schedule.course_class?.course?.name ?? 'ini'}"? Tindakan ini tidak dapat dibatalkan.`
                    }
                    onConfirm={handleDelete}
                />
            </div>
        </>
    );
}

function CourseClassField({
    courseClasses,
    value,
    onChange,
    disabled,
}: {
    courseClasses: CourseClassOption[];
    value: CourseClassOption | null;
    onChange: (value: CourseClassOption | null) => void;
    disabled?: boolean;
}) {
    const groups = groupCourseClassesByDepartment(courseClasses);

    return (
        <Combobox
            items={groups}
            value={value}
            onValueChange={onChange}
            itemToStringLabel={courseClassOptionLabel}
            isItemEqualToValue={(a, b) => a.id === b.id}
            disabled={disabled}
        >
            <ComboboxInput placeholder="Pilih kelas" className="w-full" />
            <ComboboxContent>
                <ComboboxEmpty>Kelas tidak ditemukan.</ComboboxEmpty>
                <ComboboxList>
                    {(group: CourseClassGroup) => (
                        <ComboboxGroup key={group.value} items={group.items}>
                            <ComboboxLabel>{group.value}</ComboboxLabel>
                            <ComboboxCollection>
                                {(option: CourseClassOption) => (
                                    <ComboboxItem key={option.id} value={option}>
                                        <div className="flex flex-col">
                                            <span>
                                                {courseClassOptionLabel(option)}
                                            </span>
                                            <span className="text-xs text-muted-foreground">
                                                {option.lecturer?.user?.profile
                                                    ?.full_name ?? 'N/A'}
                                                {option.academic_term &&
                                                    ` • ${formatAcademicTermLabel(option.academic_term)}`}
                                            </span>
                                        </div>
                                    </ComboboxItem>
                                )}
                            </ComboboxCollection>
                        </ComboboxGroup>
                    )}
                </ComboboxList>
            </ComboboxContent>
        </Combobox>
    );
}

function CreateForm({
    open,
    onOpenChange,
    courseClasses,
}: {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    courseClasses: CourseClassOption[];
}) {
    const [courseClass, setCourseClass] = useState<CourseClassOption | null>(
        null,
    );

    return (
        <FormDialog
            open={open}
            onOpenChange={onOpenChange}
            title="Tambah Jadwal"
            action={store()}
            resetOnSuccess
            onSuccess={() => {
                onOpenChange(false);
                setCourseClass(null);
            }}
        >
            {({ errors }) => (
                <div className="grid gap-4">
                    <div className="grid gap-2">
                        <Label>
                            Kelas <span className="text-destructive">*</span>
                        </Label>
                        <input
                            type="hidden"
                            name="course_class_id"
                            value={courseClass?.id ?? ''}
                        />
                        <CourseClassField
                            courseClasses={courseClasses}
                            value={courseClass}
                            onChange={setCourseClass}
                        />
                        <InputError message={errors.course_class_id} />
                    </div>
                    <div className="grid gap-2">
                        <Label>
                            Hari <span className="text-destructive">*</span>
                        </Label>
                        <input type="hidden" name="day_of_week" />
                        <Select name="day_of_week">
                            <SelectTrigger className="w-full">
                                <SelectValue placeholder="Pilih hari" />
                            </SelectTrigger>
                            <SelectContent>
                                {DaysOfWeek.map((day) => (
                                    <SelectItem key={day} value={day}>
                                        {DayOfWeekLabels[day]}
                                    </SelectItem>
                                ))}
                            </SelectContent>
                        </Select>
                        <InputError message={errors.day_of_week} />
                    </div>
                    <div className="grid grid-cols-2 gap-4">
                        <div className="grid gap-2">
                            <Label htmlFor="start_time">
                                Jam Mulai{' '}
                                <span className="text-destructive">*</span>
                            </Label>
                            <Input
                                id="start_time"
                                name="start_time"
                                type="time"
                            />
                            <InputError message={errors.start_time} />
                        </div>
                        <div className="grid gap-2">
                            <Label htmlFor="end_time">
                                Jam Selesai{' '}
                                <span className="text-destructive">*</span>
                            </Label>
                            <Input id="end_time" name="end_time" type="time" />
                            <InputError message={errors.end_time} />
                        </div>
                    </div>
                    <div className="grid gap-2">
                        <Label htmlFor="room">Ruang</Label>
                        <Input
                            id="room"
                            name="room"
                            placeholder="Contoh: R.301"
                        />
                        <InputError message={errors.room} />
                    </div>
                    <div className="grid gap-2">
                        <Label htmlFor="online_link">Link Online</Label>
                        <Input
                            id="online_link"
                            name="online_link"
                            placeholder="https://meet.google.com/xxx-xxxx"
                        />
                        <InputError message={errors.online_link} />
                    </div>
                </div>
            )}
        </FormDialog>
    );
}

function EditForm({
    open,
    onOpenChange,
    editing,
    courseClasses,
}: {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    editing: Schedule | null;
    courseClasses: CourseClassOption[];
}) {
    const [courseClass, setCourseClass] = useState<CourseClassOption | null>(
        editing
            ? (courseClasses.find((c) => c.id === editing.course_class_id) ??
                  null)
            : null,
    );

    return (
        <FormDialog
            open={open}
            onOpenChange={onOpenChange}
            title="Edit Jadwal"
            action={editing ? update(editing.id) : ''}
            resetOnSuccess
            onSuccess={() => onOpenChange(false)}
        >
            {({ errors }) =>
                editing && (
                    <div className="grid gap-4">
                        <div className="grid gap-2">
                            <Label>
                                Kelas{' '}
                                <span className="text-destructive">*</span>
                            </Label>
                            <input
                                type="hidden"
                                name="course_class_id"
                                value={courseClass?.id ?? ''}
                            />
                            <CourseClassField
                                courseClasses={courseClasses}
                                value={courseClass}
                                onChange={setCourseClass}
                            />
                            <InputError message={errors.course_class_id} />
                        </div>
                        <div className="grid gap-2">
                            <Label>
                                Hari{' '}
                                <span className="text-destructive">*</span>
                            </Label>
                            <Select
                                name="day_of_week"
                                defaultValue={editing.day_of_week ?? undefined}
                            >
                                <SelectTrigger className="w-full">
                                    <SelectValue placeholder="Pilih hari" />
                                </SelectTrigger>
                                <SelectContent>
                                    {DaysOfWeek.map((day) => (
                                        <SelectItem key={day} value={day}>
                                            {DayOfWeekLabels[day]}
                                        </SelectItem>
                                    ))}
                                </SelectContent>
                            </Select>
                            <InputError message={errors.day_of_week} />
                        </div>
                        <div className="grid grid-cols-2 gap-4">
                            <div className="grid gap-2">
                                <Label htmlFor="edit-start_time">
                                    Jam Mulai{' '}
                                    <span className="text-destructive">
                                        *
                                    </span>
                                </Label>
                                <Input
                                    id="edit-start_time"
                                    name="start_time"
                                    type="time"
                                    defaultValue={toTimeInput(
                                        editing.start_time,
                                    )}
                                />
                                <InputError message={errors.start_time} />
                            </div>
                            <div className="grid gap-2">
                                <Label htmlFor="edit-end_time">
                                    Jam Selesai{' '}
                                    <span className="text-destructive">
                                        *
                                    </span>
                                </Label>
                                <Input
                                    id="edit-end_time"
                                    name="end_time"
                                    type="time"
                                    defaultValue={toTimeInput(editing.end_time)}
                                />
                                <InputError message={errors.end_time} />
                            </div>
                        </div>
                        <div className="grid gap-2">
                            <Label htmlFor="edit-room">Ruang</Label>
                            <Input
                                id="edit-room"
                                name="room"
                                placeholder="Contoh: R.301"
                                defaultValue={editing.room ?? ''}
                            />
                            <InputError message={errors.room} />
                        </div>
                        <div className="grid gap-2">
                            <Label htmlFor="edit-online_link">
                                Link Online
                            </Label>
                            <Input
                                id="edit-online_link"
                                name="online_link"
                                placeholder="https://meet.google.com/xxx-xxxx"
                                defaultValue={editing.online_link ?? ''}
                            />
                            <InputError message={errors.online_link} />
                        </div>
                    </div>
                )
            }
        </FormDialog>
    );
}
