import { Link, router, usePage } from "@inertiajs/react";
import {
    cloneElement,
    isValidElement,
    useEffect,
    useId,
    useRef,
    useState,
    type ButtonHTMLAttributes,
    type FormEvent,
    type ReactElement,
    type ReactNode,
    type CSSProperties,
} from "react";
import type { Course, Material, Paginated, Shared } from "../types";

const assets: Record<string, string> = {
    book: "dc9f0",
    dashboard: "5b47a",
    clipboard: "21d30",
    folder: "df40d",
    chart: "aa73a",
    logout: "d20b3",
    users: "ada46",
    graduation: "99c21",
    plus: "fbc1d",
    search: "53e3f",
    filter: "9faab",
    menu: "05ab7",
    file: "ae99c",
    warning: "6c73b",
    ellipsis: "a9fac",
};
export function Icon({
    name,
    className = "",
}: {
    name: string;
    className?: string;
}) {
    const path = assets[name]
        ? `/icons/figma-${assets[name]}.svg`
        : `/icons/${name}.svg`;
    return (
        <span
            aria-hidden="true"
            className={`icon ${className}`}
            style={{ "--icon-url": `url("${path}")` } as CSSProperties}
        />
    );
}
export function Button({
    variant = "primary",
    loading = false,
    icon,
    children,
    ...rest
}: ButtonHTMLAttributes<HTMLButtonElement> & {
    variant?: "primary" | "secondary" | "danger" | "ghost";
    loading?: boolean;
    icon?: string;
}) {
    return (
        <button
            {...rest}
            disabled={rest.disabled || loading}
            aria-busy={loading}
            className={`button ${variant} ${rest.className ?? ""}`}
        >
            {loading ? (
                <span className="spinner" />
            ) : icon ? (
                <Icon name={icon} />
            ) : null}
            {children}
        </button>
    );
}
export function ButtonLink({
    href,
    children,
    icon,
    secondary = false,
}: {
    href: string;
    children: ReactNode;
    icon?: string;
    secondary?: boolean;
}) {
    return (
        <Link
            className={`button ${secondary ? "secondary" : "primary"}`}
            href={href}
        >
            {icon && <Icon name={icon} />} {children}
        </Link>
    );
}
export function Badge({
    children,
    tone = "neutral",
}: {
    children: ReactNode;
    tone?: "neutral" | "success" | "warning" | "danger" | "info";
}) {
    return <span className={`badge ${tone}`}>{children}</span>;
}
export function Field({
    label,
    name,
    children,
    hint,
}: {
    label: string;
    name?: string;
    children: ReactNode;
    hint?: string;
}) {
    const { errors } = usePage<Shared>().props;
    const generated = useId();
    const input = isValidElement(children)
        ? (children as ReactElement<{
              id?: string;
              name?: string;
              "aria-describedby"?: string;
              "aria-invalid"?: boolean;
          }>)
        : null;
    const id = input?.props.id ?? generated;
    const key = name ?? input?.props.name;
    const error = key ? errors[key] : null;
    return (
        <div className="field">
            <label className="label" htmlFor={id}>
                {label}
            </label>
            {input
                ? cloneElement(input, {
                      id,
                      "aria-describedby":
                          [hint ? `${id}-hint` : "", error ? `${id}-error` : ""]
                              .filter(Boolean)
                              .join(" ") || undefined,
                      "aria-invalid": !!error,
                  })
                : children}
            {hint && (
                <span id={`${id}-hint`} className="small muted">
                    {hint}
                </span>
            )}
            {error && (
                <span id={`${id}-error`} className="field-error">
                    {error}
                </span>
            )}
        </div>
    );
}
export function Errors() {
    const { errors } = usePage<Shared>().props;
    const messages = [...new Set(Object.values(errors))];
    return messages.length ? (
        <div className="alert danger" role="alert">
            <Icon name="warning" />
            <div>
                <strong>Check these details</strong>
                <ul>
                    {messages.map((e) => (
                        <li key={e}>{e}</li>
                    ))}
                </ul>
            </div>
        </div>
    ) : null;
}
export function Alert({
    title,
    children,
    tone = "info",
}: {
    title: string;
    children?: ReactNode;
    tone?: "warning" | "info" | "danger" | "success";
}) {
    return (
        <div
            className={`alert ${tone}`}
            role={tone === "danger" ? "alert" : "status"}
        >
            <Icon
                name={
                    tone === "warning" || tone === "danger"
                        ? "warning"
                        : "circle-check"
                }
            />
            <div>
                <strong>{title}</strong>
                {children && <div>{children}</div>}
            </div>
        </div>
    );
}
export function Empty({
    title = "No records yet",
    children,
}: {
    title?: string;
    children?: ReactNode;
}) {
    return (
        <div className="empty">
            <Icon name="folder" />
            <h3>{title}</h3>
            <p>
                {children ??
                    "Records will appear here when they have been added."}
            </p>
        </div>
    );
}
export function Heading({
    title,
    description,
    action,
}: {
    title: ReactNode;
    description?: string;
    action?: ReactNode;
}) {
    return (
        <div className="page-heading">
            <div>
                <h1>{title}</h1>
                {description && <p>{description}</p>}
            </div>
            {action}
        </div>
    );
}
export function Panel({
    title,
    children,
    action,
    className = "",
}: {
    title?: string;
    children: ReactNode;
    action?: ReactNode;
    className?: string;
}) {
    return (
        <section className={`panel ${className}`}>
            {title && (
                <div className="section-heading">
                    <h3>{title}</h3>
                    {action}
                </div>
            )}
            {children}
        </section>
    );
}
export function Stat({
    label,
    value,
    hint,
}: {
    label: string;
    value: number | string;
    hint?: string;
}) {
    return (
        <div className="stat">
            <span className="small strong muted">{label}</span>
            <strong className="stat-value">{value}</strong>
            <span className="small muted">
                {hint ?? "Selected academic period"}
            </span>
        </div>
    );
}
export function Pagination<T>({ page }: { page: Paginated<T> }) {
    return (
        <div className="table-footer">
            <span className="small muted">
                {page.total
                    ? `Showing ${page.from}–${page.to} of ${page.total} records`
                    : "No records"}
            </span>
            <div className="actions">
                {page.prev_page_url ? (
                    <ButtonLink href={page.prev_page_url} secondary>
                        Previous
                    </ButtonLink>
                ) : (
                    <Button disabled variant="secondary">
                        Previous
                    </Button>
                )}
                {page.next_page_url ? (
                    <ButtonLink href={page.next_page_url} secondary>
                        Next
                    </ButtonLink>
                ) : (
                    <Button disabled variant="secondary">
                        Next
                    </Button>
                )}
            </div>
        </div>
    );
}
export function Search({
    value = "",
    placeholder = "Search records",
    url,
    children,
}: {
    value?: string;
    placeholder?: string;
    url: string;
    children?: ReactNode;
}) {
    return (
        <form
            className="toolbar"
            onSubmit={(e) => {
                e.preventDefault();
                router.get(
                    url,
                    Object.fromEntries(
                        new FormData(e.currentTarget).entries(),
                    ) as Record<string, string>,
                    { preserveState: true },
                );
            }}
        >
            <label className="search">
                <Icon name="search" />
                <input
                    aria-label={placeholder}
                    name="search"
                    defaultValue={value}
                    placeholder={placeholder}
                />
            </label>
            {children}
            <Button variant="secondary" icon="search">
                Search
            </Button>
        </form>
    );
}
export function SaveForm({
    url,
    method = "post",
    children,
    label = "Save changes",
    onSuccess,
    transform,
    reset = true,
    disabled = false,
    variant = "primary",
}: {
    url: string;
    method?: "post" | "put";
    children?: ReactNode;
    label?: string;
    onSuccess?: () => void;
    transform?: (data: FormData) => void;
    reset?: boolean;
    disabled?: boolean;
    variant?: "primary" | "danger";
}) {
    const [busy, setBusy] = useState(false);
    return (
        <form
            className="form-stack"
            onSubmit={(e: FormEvent<HTMLFormElement>) => {
                e.preventDefault();
                if (busy) return;
                const form = e.currentTarget;
                const data = new FormData(form);
                if (method === "put") data.set("_method", "PUT");
                transform?.(data);
                setBusy(true);
                router.post(url, data, {
                    preserveScroll: true,
                    onSuccess: () => {
                        if (reset) form.reset();
                        onSuccess?.();
                    },
                    onFinish: () => setBusy(false),
                });
            }}
        >
            {children}
            <div className="actions">
                <Button
                    loading={busy}
                    disabled={disabled}
                    type="submit"
                    variant={variant}
                >
                    {label}
                </Button>
            </div>
        </form>
    );
}
export function Dialog({
    title,
    children,
    onClose,
}: {
    title: string;
    children: ReactNode;
    onClose: () => void;
}) {
    const ref = useRef<HTMLDialogElement>(null);
    useEffect(() => {
        const dialog = ref.current;
        const opener = document.activeElement as HTMLElement | null;
        dialog?.showModal();
        return () => {
            dialog?.close();
            const target = opener?.isConnected
                ? opener
                : document.getElementById("main");
            target?.focus({ preventScroll: true });
        };
    }, []);
    return (
        <dialog
            ref={ref}
            onCancel={onClose}
            onClick={(e) => {
                if (e.target === e.currentTarget) onClose();
            }}
        >
            <div className="dialog-heading">
                <h2>{title}</h2>
                <Button
                    variant="ghost"
                    type="button"
                    aria-label="Close dialog"
                    onClick={onClose}
                >
                    <Icon name="x" />
                </Button>
            </div>
            {children}
        </dialog>
    );
}
export function Confirm({
    title,
    children,
    url,
    onClose,
}: {
    title: string;
    children: ReactNode;
    url: string;
    onClose: () => void;
}) {
    return (
        <Dialog title={title} onClose={onClose}>
            <p>{children}</p>
            <Errors />
            <SaveForm url={url} label="Confirm" onSuccess={onClose} />
        </Dialog>
    );
}
export function CourseCard({
    course,
    student = false,
}: {
    course: Course;
    student?: boolean;
    materials?: boolean;
}) {
    return (
        <article className="class-card">
            <div className="overline">
                <Icon name="book" />
                {course.subject.code}
            </div>
            <h3>{course.subject.name}</h3>
            <p>
                {course.roster_count !== undefined
                    ? course.roster_count + " Students · "
                    : ""}
                Class {course.section}
            </p>
            <small className="muted">{course.period.name}</small>
            <p>{course.schedule || "Timetable awaiting review"}</p>
            <div className="actions">
                <ButtonLink href={"/classes/" + course.id} icon="clipboard">
                    {student
                        ? "View subject and final grade"
                        : "View roster and final grades"}
                </ButtonLink>
            </div>
        </article>
    );
}
export function MaterialCard({ material }: { material: Material }) {
    return (
        <article className="material-card">
            <div className="overline">
                <Icon name="file" />
                <Badge tone="info">{material.url ? "Link" : "File"}</Badge>
            </div>
            <h3>{material.title}</h3>
            <p>
                {material.course?.subject.name
                    ? `${material.course.subject.name} · `
                    : ""}
                {material.topic}
            </p>
            <a
                href={`/materials/${material.id}/download`}
                target="_blank"
                rel="noreferrer"
            >
                Open material
            </a>
        </article>
    );
}
export function Table({
    columns,
    children,
}: {
    columns: string[];
    children: ReactNode;
}) {
    return (
        <div className="table-scroll" tabIndex={0}>
            <table>
                <thead>
                    <tr>
                        {columns.map((c) => (
                            <th key={c} scope="col">
                                {c}
                            </th>
                        ))}
                    </tr>
                </thead>
                <tbody>{children}</tbody>
            </table>
        </div>
    );
}
