import type { FormEvent } from 'react';
import { useMemo, useState } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { ChevronDown, GraduationCap, Loader2, Pencil, Plus, UserPlus, Users, X } from 'lucide-react';

import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { ImageUpload } from '@/components/common/ImageUpload';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { ModuleHeader } from '@/components/ui/module-header';
import { NativeSelect } from '@/components/ui/native-select';
import { PageSkeleton } from '@/components/ui/page-skeleton';
import { UserAvatar } from '@/components/common/UserAvatar';
import { useAuth } from '@/contexts/useAuth';
import { usePermissions } from '@/hooks/usePermissions';
import {
  createUser,
  getClass,
  linkGuardianStudent,
  listGuardianStudents,
  listRoles,
  listUsers,
  updateUser,
  type AdminUserRow,
} from '@/business/my-class/api';
import {
  buildMyClassStudentSaveData,
  buildMyClassGuardianSaveData,
  canManageMyClassStudents,
  hasMyClassGuardianFormValues,
  type MyClassGuardianFormValues,
  type MyClassStudentFormValues,
} from '@/business/my-class/selectors';
import { USER_NAME_PREFIX_OPTIONS } from '@/shared/constants/users';
import { getErrorMessage } from '@/shared/errors/errorMessages';
import { cn } from '@/lib/utils';

function personName(row: { firstName?: string | null; lastName?: string | null; email?: string }): string {
  return [row.firstName, row.lastName].filter(Boolean).join(' ').trim() || row.email || '—';
}

interface StatusMessage {
  readonly type: 'success' | 'error';
  readonly text: string;
}

const emptyStudentForm = (): MyClassStudentFormValues => ({
  namePrefix: '',
  firstName: '',
  lastName: '',
  email: '',
  phoneNumber: '',
  avatar: null,
  guardians: [emptyGuardianForm()],
});

function emptyGuardianForm(): MyClassGuardianFormValues {
  return {
    key: `guardian-${Date.now()}-${Math.random().toString(36).slice(2)}`,
    id: null,
    namePrefix: '',
    firstName: '',
    lastName: '',
    email: '',
    phoneNumber: '',
    avatar: null,
  };
}

function studentFormFromRow(
  row: AdminUserRow,
  guardians: readonly GuardianStudentLink[] = [],
): MyClassStudentFormValues {
  const guardianForms = guardians
    .map((link): MyClassGuardianFormValues | null => {
      const guardian = link.guardian;
      if (!guardian?.id) return null;
      return {
        key: link.id,
        id: guardian.id,
        namePrefix: guardian.name_prefix ?? '',
        firstName: guardian.firstName ?? '',
        lastName: guardian.lastName ?? '',
        email: guardian.email ?? '',
        phoneNumber: guardian.phoneNumber ?? '',
        avatar: guardian.avatar?.[0]?.privateUrl ?? null,
      };
    })
    .filter((guardian): guardian is MyClassGuardianFormValues => Boolean(guardian));

  return {
    namePrefix: row.name_prefix ?? '',
    firstName: row.firstName ?? '',
    lastName: row.lastName ?? '',
    email: row.email,
    phoneNumber: row.phoneNumber ?? '',
    avatar: row.avatar?.[0]?.privateUrl ?? null,
    guardians: guardianForms.length > 0 ? guardianForms : [emptyGuardianForm()],
  };
}

type GuardianStudentLink = NonNullable<Awaited<ReturnType<typeof listGuardianStudents>>['rows'][number]>;

export default function MyClassPage() {
  const { user } = useAuth();
  const permissions = usePermissions();
  const queryClient = useQueryClient();
  const classId = user?.classId ?? null;
  const [studentForm, setStudentForm] = useState<MyClassStudentFormValues>(() => emptyStudentForm());
  const [editingStudentId, setEditingStudentId] = useState<string | null>(null);
  const [isStudentFormOpen, setIsStudentFormOpen] = useState(false);
  const [studentFormSaving, setStudentFormSaving] = useState(false);
  const [studentFormStatus, setStudentFormStatus] = useState<StatusMessage | null>(null);

  const classQuery = useQuery({
    queryKey: ['my-class', classId],
    queryFn: () => getClass(classId as string),
    enabled: Boolean(classId),
  });
  const membersQuery = useQuery({
    queryKey: ['my-class-members', classId],
    queryFn: () => listUsers({ classId: classId as string }),
    enabled: Boolean(classId),
  });
  const guardiansQuery = useQuery({
    queryKey: ['my-class-guardians'],
    queryFn: () => listGuardianStudents(),
    enabled: Boolean(classId),
  });
  const rolesQuery = useQuery({
    queryKey: ['roles'],
    queryFn: listRoles,
    enabled: Boolean(classId) && (permissions.has('CREATE_USERS') || permissions.has('UPDATE_USERS')),
  });

  const members = useMemo(
    () => membersQuery.data?.rows ?? [],
    [membersQuery.data],
  );
  const students = useMemo(
    () => members.filter((m) => m.app_role?.name === 'student'),
    [members],
  );
  const staff = useMemo(
    () => members.filter((m) => m.app_role?.name === 'teacher' || m.app_role?.name === 'support_staff'),
    [members],
  );
  const studentRoleId = useMemo(
    () => rolesQuery.data?.rows.find((role) => role.name === 'student')?.id ?? null,
    [rolesQuery.data],
  );
  const guardianRoleId = useMemo(
    () => rolesQuery.data?.rows.find((role) => role.name === 'guardian')?.id ?? null,
    [rolesQuery.data],
  );
  const canCreateStudents = canManageMyClassStudents({
    hasUserPermission: permissions.has('CREATE_USERS'),
    classId,
    studentRoleId,
  });
  const canUpdateStudents = canManageMyClassStudents({
    hasUserPermission: permissions.has('UPDATE_USERS'),
    classId,
    studentRoleId,
  });

  // studentId → guardian display names.
  const guardiansByStudent = useMemo(() => {
    const map = new Map<string, string[]>();
    for (const row of guardiansQuery.data?.rows ?? []) {
      const name = row.guardian ? personName(row.guardian) : null;
      if (!name) continue;
      const list = map.get(row.studentId) ?? [];
      list.push(name);
      map.set(row.studentId, list);
    }
    return map;
  }, [guardiansQuery.data]);
  const guardianLinksByStudent = useMemo(() => {
    const map = new Map<string, GuardianStudentLink[]>();
    for (const row of guardiansQuery.data?.rows ?? []) {
      const current = map.get(row.studentId) ?? [];
      map.set(row.studentId, [...current, row]);
    }
    return map;
  }, [guardiansQuery.data]);

  const updateStudentForm = (patch: Partial<MyClassStudentFormValues>) => {
    setStudentForm((current) => ({ ...current, ...patch }));
    setStudentFormStatus(null);
  };
  const updateGuardianForm = (guardianKey: string, patch: Partial<MyClassGuardianFormValues>) => {
    setStudentForm((current) => ({
      ...current,
      guardians: current.guardians.map((guardian) => (
        guardian.key === guardianKey ? { ...guardian, ...patch } : guardian
      )),
    }));
    setStudentFormStatus(null);
  };
  const addGuardianForm = () => {
    setStudentForm((current) => ({
      ...current,
      guardians: [...current.guardians, emptyGuardianForm()],
    }));
    setStudentFormStatus(null);
  };
  const removeGuardianForm = (guardianKey: string) => {
    setStudentForm((current) => ({
      ...current,
      guardians: current.guardians.length === 1
        ? [emptyGuardianForm()]
        : current.guardians.filter((guardian) => guardian.key !== guardianKey),
    }));
    setStudentFormStatus(null);
  };

  const resetStudentForm = () => {
    setStudentForm(emptyStudentForm());
    setEditingStudentId(null);
    setIsStudentFormOpen(false);
  };

  const startCreateStudent = () => {
    setStudentFormStatus(null);
    if (isStudentFormOpen && !editingStudentId) {
      resetStudentForm();
      return;
    }
    setStudentForm(emptyStudentForm());
    setEditingStudentId(null);
    setIsStudentFormOpen(true);
  };

  const startEditStudent = (student: AdminUserRow) => {
    const guardians = guardianLinksByStudent.get(student.id) ?? [];
    setStudentForm(studentFormFromRow(student, guardians));
    setEditingStudentId(student.id);
    setStudentFormStatus(null);
    setIsStudentFormOpen(true);
  };

  const handleStudentSubmit = async (event: FormEvent) => {
    event.preventDefault();
    setStudentFormStatus(null);

    if (!classId || !studentRoleId || !guardianRoleId) {
      setStudentFormStatus({ type: 'error', text: 'Student access is not available for this class.' });
      return;
    }
    const guardiansToSave = studentForm.guardians.filter(hasMyClassGuardianFormValues);
    if (guardiansToSave.some((guardian) => guardian.email.trim() === '')) {
      setStudentFormStatus({ type: 'error', text: 'Guardian email is required for each entered guardian.' });
      return;
    }

    const payload = buildMyClassStudentSaveData(studentForm, classId, studentRoleId);

    setStudentFormSaving(true);
    try {
      let studentId = editingStudentId;
      if (editingStudentId) {
        await updateUser(editingStudentId, payload);
      } else {
        const created = await createUser(payload);
        studentId = created.id;
      }

      if (studentId) {
        for (const guardian of guardiansToSave) {
          const guardianPayload = buildMyClassGuardianSaveData(guardian, guardianRoleId);
          const guardianId = guardian.id
            ? guardian.id
            : (await createUser(guardianPayload)).id;
          if (guardian.id) {
            await updateUser(guardian.id, guardianPayload);
          }
          if (guardianId) {
            await linkGuardianStudent(guardianId, studentId);
          }
        }
      }

      await Promise.all([
        queryClient.invalidateQueries({ queryKey: ['my-class-members', classId] }),
        queryClient.invalidateQueries({ queryKey: ['my-class-guardians'] }),
      ]);
      setStudentFormStatus({
        type: 'success',
        text: editingStudentId ? 'Student updated.' : 'Student created.',
      });
      resetStudentForm();
    } catch (error) {
      setStudentFormStatus({ type: 'error', text: getErrorMessage(error, 'Could not save student') });
    } finally {
      setStudentFormSaving(false);
    }
  };

  if (!classId) {
    return (
      <div className="p-4 md:p-6">
        <div className="rounded-lg border border-slate-700/50 bg-slate-800/40 px-4 py-3 text-sm text-slate-300">
          You are not assigned to a class yet.
        </div>
      </div>
    );
  }

  if (classQuery.isLoading || membersQuery.isLoading || rolesQuery.isLoading) {
    return <PageSkeleton />;
  }

  const formControlClassName =
    'border-slate-600 bg-slate-950/80 text-slate-100 placeholder:text-slate-500 focus-visible:ring-lime-400 focus-visible:ring-offset-slate-950';

  return (
    <div className="space-y-6 p-4 md:p-6">
      <ModuleHeader
        title={classQuery.data?.name ?? 'My Class'}
        description="Your class roster and linked guardians."
        icon={GraduationCap}
        iconClassName="bg-gradient-to-br from-lime-500 to-lime-700"
      />

      <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
        <Card className="lg:col-span-2">
          <CardHeader>
            <div className="flex flex-wrap items-center justify-between gap-3">
              <CardTitle className="flex items-center gap-2 text-base">
                <Users size={16} />
                Students ({students.length})
              </CardTitle>
              {canCreateStudents && (
                <Button type="button" size="sm" onClick={startCreateStudent}>
                  <UserPlus className="mr-2 h-4 w-4" />
                  {isStudentFormOpen && !editingStudentId ? 'Hide form' : 'Add student'}
                  <ChevronDown
                    className={cn(
                      'ml-2 h-4 w-4 transition-transform',
                      isStudentFormOpen && !editingStudentId && 'rotate-180',
                    )}
                  />
                </Button>
              )}
            </div>
          </CardHeader>
          <CardContent className="space-y-4">
            {studentFormStatus && (
              <div
                className={`rounded-lg border px-3 py-2 text-sm ${
                  studentFormStatus.type === 'success'
                    ? 'border-emerald-500/30 bg-emerald-500/10 text-emerald-200'
                    : 'border-red-500/30 bg-red-500/10 text-red-200'
                }`}
              >
                {studentFormStatus.text}
              </div>
            )}

            {isStudentFormOpen && (
              <form
                className="space-y-4 rounded-lg border border-slate-600/80 bg-slate-950/45 p-4"
                onSubmit={handleStudentSubmit}
              >
                <div className="flex flex-wrap items-center justify-between gap-3">
                  <div>
                    <p className="text-sm font-semibold text-slate-100">
                      {editingStudentId ? 'Edit student' : 'Add student'}
                    </p>
                  </div>
                  <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    onClick={resetStudentForm}
                    className="text-slate-300"
                  >
                    <X className="mr-2 h-4 w-4" />
                    Cancel
                  </Button>
                </div>

                <div className="grid gap-4 sm:grid-cols-[auto_1fr]">
                  <ImageUpload
                    value={studentForm.avatar}
                    onChange={(avatar) => updateStudentForm({ avatar })}
                    table="users"
                    field="avatar"
                    label="Avatar"
                    shape="square"
                    previewSize="lg"
                  />
                  <div className="grid gap-4 sm:grid-cols-4">
                    <div className="space-y-1.5">
                      <Label htmlFor="student-prefix" className="text-slate-100">Title</Label>
                      <NativeSelect
                        id="student-prefix"
                        value={studentForm.namePrefix}
                        className={formControlClassName}
                        onChange={(event) => updateStudentForm({ namePrefix: event.target.value })}
                      >
                        <option value="">None</option>
                        {USER_NAME_PREFIX_OPTIONS.map((option) => (
                          <option key={option.value} value={option.value}>{option.label}</option>
                        ))}
                      </NativeSelect>
                    </div>
                    <div className="space-y-1.5">
                      <Label htmlFor="student-first-name" className="text-slate-100">First name</Label>
                      <Input
                        id="student-first-name"
                        value={studentForm.firstName}
                        className={formControlClassName}
                        onChange={(event) => updateStudentForm({ firstName: event.target.value })}
                      />
                    </div>
                    <div className="space-y-1.5">
                      <Label htmlFor="student-last-name" className="text-slate-100">Last name</Label>
                      <Input
                        id="student-last-name"
                        value={studentForm.lastName}
                        className={formControlClassName}
                        onChange={(event) => updateStudentForm({ lastName: event.target.value })}
                      />
                    </div>
                    <div className="space-y-1.5">
                      <Label htmlFor="student-email" className="text-slate-100">Email</Label>
                      <Input
                        id="student-email"
                        type="email"
                        value={studentForm.email}
                        className={formControlClassName}
                        onChange={(event) => updateStudentForm({ email: event.target.value })}
                        required
                      />
                    </div>
                    <div className="space-y-1.5 sm:col-span-2">
                      <Label htmlFor="student-phone" className="text-slate-100">Phone number</Label>
                      <Input
                        id="student-phone"
                        type="tel"
                        value={studentForm.phoneNumber}
                        className={formControlClassName}
                        onChange={(event) => updateStudentForm({ phoneNumber: event.target.value })}
                      />
                    </div>
                  </div>
                </div>

                <div className="space-y-4 rounded-lg border border-slate-700/70 bg-slate-950/35 p-4">
                  <div className="flex flex-wrap items-center justify-between gap-3">
                    <p className="text-sm font-semibold text-slate-100">Guardians</p>
                    <Button type="button" variant="outline" size="sm" onClick={addGuardianForm}>
                      <Plus className="mr-2 h-4 w-4" />
                      Add guardian
                    </Button>
                  </div>
                  {studentForm.guardians.map((guardian, index) => (
                    <div
                      key={guardian.key}
                      className="space-y-4 rounded-lg border border-slate-700/70 bg-slate-950/45 p-4"
                    >
                      <div className="flex items-center justify-between gap-3">
                        <p className="text-sm font-semibold text-slate-200">Guardian {index + 1}</p>
                        {guardian.id ? (
                          <span className="text-xs text-slate-400">Linked guardian</span>
                        ) : (
                          <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            onClick={() => removeGuardianForm(guardian.key)}
                            className="text-slate-300"
                          >
                            <X className="mr-2 h-4 w-4" />
                            Remove
                          </Button>
                        )}
                      </div>
                      <div className="grid gap-4 sm:grid-cols-[auto_1fr]">
                        <ImageUpload
                          value={guardian.avatar}
                          onChange={(avatar) => updateGuardianForm(guardian.key, { avatar })}
                          table="users"
                          field="avatar"
                          label="Photo"
                          shape="square"
                          previewSize="lg"
                        />
                        <div className="grid gap-4 sm:grid-cols-4">
                          <div className="space-y-1.5">
                            <Label htmlFor={`guardian-prefix-${guardian.key}`} className="text-slate-100">
                              Title
                            </Label>
                            <NativeSelect
                              id={`guardian-prefix-${guardian.key}`}
                              value={guardian.namePrefix}
                              className={formControlClassName}
                              onChange={(event) => updateGuardianForm(guardian.key, { namePrefix: event.target.value })}
                            >
                              <option value="">None</option>
                              {USER_NAME_PREFIX_OPTIONS.map((option) => (
                                <option key={option.value} value={option.value}>{option.label}</option>
                              ))}
                            </NativeSelect>
                          </div>
                          <div className="space-y-1.5">
                            <Label htmlFor={`guardian-first-name-${guardian.key}`} className="text-slate-100">
                              First name
                            </Label>
                            <Input
                              id={`guardian-first-name-${guardian.key}`}
                              value={guardian.firstName}
                              className={formControlClassName}
                              onChange={(event) => updateGuardianForm(guardian.key, { firstName: event.target.value })}
                            />
                          </div>
                          <div className="space-y-1.5">
                            <Label htmlFor={`guardian-last-name-${guardian.key}`} className="text-slate-100">
                              Last name
                            </Label>
                            <Input
                              id={`guardian-last-name-${guardian.key}`}
                              value={guardian.lastName}
                              className={formControlClassName}
                              onChange={(event) => updateGuardianForm(guardian.key, { lastName: event.target.value })}
                            />
                          </div>
                          <div className="space-y-1.5">
                            <Label htmlFor={`guardian-email-${guardian.key}`} className="text-slate-100">
                              Email
                            </Label>
                            <Input
                              id={`guardian-email-${guardian.key}`}
                              type="email"
                              value={guardian.email}
                              className={formControlClassName}
                              onChange={(event) => updateGuardianForm(guardian.key, { email: event.target.value })}
                            />
                          </div>
                          <div className="space-y-1.5 sm:col-span-2">
                            <Label htmlFor={`guardian-phone-${guardian.key}`} className="text-slate-100">
                              Phone number
                            </Label>
                            <Input
                              id={`guardian-phone-${guardian.key}`}
                              type="tel"
                              value={guardian.phoneNumber}
                              className={formControlClassName}
                              onChange={(event) => updateGuardianForm(guardian.key, { phoneNumber: event.target.value })}
                            />
                          </div>
                        </div>
                      </div>
                    </div>
                  ))}
                </div>

                <div className="flex justify-end">
                  <Button
                    type="submit"
                    disabled={studentFormSaving || (editingStudentId ? !canUpdateStudents : !canCreateStudents)}
                  >
                    {studentFormSaving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
                    {editingStudentId ? 'Save changes' : 'Create student'}
                  </Button>
                </div>
              </form>
            )}

            {students.length === 0 ? (
              <p className="text-sm text-slate-400">No students in this class yet.</p>
            ) : (
              <ul className="divide-y divide-slate-700/50">
                {students.map((s) => {
                  const guardians = guardiansByStudent.get(s.id) ?? [];
                  return (
                    <li key={s.id} className="flex items-center justify-between gap-3 py-3">
                      <div className="flex min-w-0 items-center gap-3">
                        <UserAvatar
                          name={personName(s)}
                          avatarUrl={s.avatar?.[0]?.privateUrl ?? null}
                          className="h-9 w-9 text-[11px]"
                        />
                        <div className="min-w-0">
                          <p className="truncate text-sm font-medium text-slate-200">{personName(s)}</p>
                          <p className="truncate text-xs text-slate-500">
                            {guardians.length > 0 ? `Guardians: ${guardians.join(', ')}` : 'No guardian linked'}
                          </p>
                        </div>
                      </div>
                      {canUpdateStudents && (
                        <Button
                          type="button"
                          variant="ghost"
                          size="sm"
                          onClick={() => startEditStudent(s)}
                          className="shrink-0"
                          aria-label={`Edit ${personName(s)}`}
                        >
                          <Pencil className="h-4 w-4" />
                        </Button>
                      )}
                    </li>
                  );
                })}
              </ul>
            )}
          </CardContent>
        </Card>

        <Card>
          <CardHeader>
            <CardTitle className="text-base">Staff</CardTitle>
          </CardHeader>
          <CardContent>
            {staff.length === 0 ? (
              <p className="text-sm text-slate-400">No staff assigned.</p>
            ) : (
              <ul className="space-y-1">
                {staff.map((m: AdminUserRow) => (
                  <li key={m.id} className="text-sm text-slate-200">
                    {personName(m)}
                    <span className="ml-2 text-xs text-slate-500">
                      {m.app_role?.name === 'teacher' ? 'Teacher' : 'Support'}
                    </span>
                  </li>
                ))}
              </ul>
            )}
          </CardContent>
        </Card>
      </div>

    </div>
  );
}
