import { useCallback, useEffect, useMemo, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBuilding, faPlus, faPenToSquare } from '@fortawesome/pro-duotone-svg-icons';
import { Button } from '@/components/base/buttons/button';
import { Badge } from '@/components/base/badges/badges';
import { Table, TableCard } from '@/components/application/table/table';
import { SlideoutMenu } from '@/components/application/slideout-menus/slideout-menu';
import { TopBar } from '@/components/layout/top-bar';
import {
    ClinicForm,
    clinicCoreToGraphQLInput,
    holidayInputsFromForm,
    requiredDocInputsFromForm,
    emptyClinicFormValues,
    type ClinicFormValues,
    type ClinicStatus,
    type DocumentType,
    type ClinicHolidayEntry,
} from '@/components/forms/clinic-form';
import { formatTimeLabel } from '@/components/application/date-picker/time-picker';
import { formatDaySelection, type DaySelection } from '@/components/application/day-selector/day-selector';
import { apiClient } from '@/lib/api-client';
import { notify } from '@/lib/toast';
import { markSetupStepComplete } from '@/lib/setup-state';

// /settings/clinics — list + add/edit slideout. Schema aligns with the
// reworked Clinic entity in helix-engage/src/objects/clinic.object.ts:
//   - openMonday..openSunday (7 BOOLEANs) for the weekly pattern
//   - opensAt/closesAt (TEXT, HH:MM) for the shared daily time range
//   - two child entities: Holiday (closures) and ClinicRequiredDocument
//     (required-doc selection per clinic)
//
// Save flow:
//   1. createClinic / updateClinic (main record)
//   2. Fire child mutations in parallel:
//      - For holidays: delete-all-recreate on edit (simple, idempotent)
//      - For required docs: diff old vs new, delete removed, create added

// -- Fetched shapes from the platform ----------------------------------------

type ClinicNode = {
    id: string;
    clinicName: string | null;
    status: ClinicStatus | null;
    addressCustom: {
        addressStreet1: string | null;
        addressStreet2: string | null;
        addressCity: string | null;
        addressState: string | null;
        addressPostcode: string | null;
    } | null;
    phone: { primaryPhoneNumber: string | null } | null;
    email: { primaryEmail: string | null } | null;
    openMonday: boolean | null;
    openTuesday: boolean | null;
    openWednesday: boolean | null;
    openThursday: boolean | null;
    openFriday: boolean | null;
    openSaturday: boolean | null;
    openSunday: boolean | null;
    opensAt: string | null;
    closesAt: string | null;
    walkInAllowed: boolean | null;
    onlineBooking: boolean | null;
    cancellationWindowHours: number | null;
    arriveEarlyMin: number | null;
    // Reverse-side collections. Platform exposes them as Relay edges.
    holidays?: {
        edges: Array<{
            node: { id: string; date: string | null; reasonLabel: string | null };
        }>;
    };
    clinicRequiredDocuments?: {
        edges: Array<{ node: { id: string; documentType: DocumentType | null } }>;
    };
};

const CLINICS_QUERY = `{
    clinics(first: 100) {
        edges {
            node {
                id
                clinicName
                status
                addressCustom {
                    addressStreet1 addressStreet2 addressCity addressState addressPostcode
                }
                phone { primaryPhoneNumber }
                email { primaryEmail }
                openMonday openTuesday openWednesday openThursday openFriday openSaturday openSunday
                opensAt closesAt
                walkInAllowed onlineBooking
                cancellationWindowHours arriveEarlyMin
                holidays(first: 50) {
                    edges { node { id date reasonLabel } }
                }
                clinicRequiredDocuments(first: 50) {
                    edges { node { id documentType } }
                }
            }
        }
    }
}`;

// -- Helpers -----------------------------------------------------------------

const toDaySelection = (c: ClinicNode): DaySelection => ({
    monday: !!c.openMonday,
    tuesday: !!c.openTuesday,
    wednesday: !!c.openWednesday,
    thursday: !!c.openThursday,
    friday: !!c.openFriday,
    saturday: !!c.openSaturday,
    sunday: !!c.openSunday,
});

const toFormValues = (clinic: ClinicNode): ClinicFormValues => ({
    clinicName: clinic.clinicName ?? '',
    addressStreet1: clinic.addressCustom?.addressStreet1 ?? '',
    addressStreet2: clinic.addressCustom?.addressStreet2 ?? '',
    addressCity: clinic.addressCustom?.addressCity ?? '',
    addressState: clinic.addressCustom?.addressState ?? '',
    addressPostcode: clinic.addressCustom?.addressPostcode ?? '',
    phone: clinic.phone?.primaryPhoneNumber ?? '',
    email: clinic.email?.primaryEmail ?? '',
    openDays: toDaySelection(clinic),
    opensAt: clinic.opensAt ?? null,
    closesAt: clinic.closesAt ?? null,
    status: clinic.status ?? 'ACTIVE',
    walkInAllowed: clinic.walkInAllowed ?? true,
    onlineBooking: clinic.onlineBooking ?? true,
    cancellationWindowHours:
        clinic.cancellationWindowHours != null ? String(clinic.cancellationWindowHours) : '',
    arriveEarlyMin: clinic.arriveEarlyMin != null ? String(clinic.arriveEarlyMin) : '',
    requiredDocumentTypes:
        clinic.clinicRequiredDocuments?.edges
            .map((e) => e.node.documentType)
            .filter((t): t is DocumentType => t !== null) ?? [],
    holidays:
        clinic.holidays?.edges
            .filter((e) => e.node.date) // date is required on create but platform may have nulls from earlier
            .map(
                (e): ClinicHolidayEntry => ({
                    id: e.node.id,
                    date: e.node.date ?? '',
                    label: e.node.reasonLabel ?? '',
                }),
            ) ?? [],
});

const statusLabel: Record<ClinicStatus, string> = {
    ACTIVE: 'Active',
    TEMPORARILY_CLOSED: 'Temporarily closed',
    PERMANENTLY_CLOSED: 'Permanently closed',
};

const statusColor: Record<ClinicStatus, 'success' | 'warning' | 'gray'> = {
    ACTIVE: 'success',
    TEMPORARILY_CLOSED: 'warning',
    PERMANENTLY_CLOSED: 'gray',
};

// Save-flow helpers — each mutation is a thin wrapper so handleSave
// reads linearly.
const createClinicMutation = (data: Record<string, unknown>) =>
    apiClient.graphql<{ createClinic: { id: string } }>(
        `mutation CreateClinic($data: ClinicCreateInput!) {
            createClinic(data: $data) { id }
        }`,
        { data },
    );

const updateClinicMutation = (id: string, data: Record<string, unknown>) =>
    apiClient.graphql<{ updateClinic: { id: string } }>(
        `mutation UpdateClinic($id: UUID!, $data: ClinicUpdateInput!) {
            updateClinic(id: $id, data: $data) { id }
        }`,
        { id, data },
    );

const createHolidayMutation = (data: Record<string, unknown>) =>
    apiClient.graphql(
        `mutation CreateHoliday($data: HolidayCreateInput!) {
            createHoliday(data: $data) { id }
        }`,
        { data },
    );

const deleteHolidayMutation = (id: string) =>
    apiClient.graphql(
        `mutation DeleteHoliday($id: UUID!) { deleteHoliday(id: $id) { id } }`,
        { id },
    );

const createRequiredDocMutation = (data: Record<string, unknown>) =>
    apiClient.graphql(
        `mutation CreateClinicRequiredDocument($data: ClinicRequiredDocumentCreateInput!) {
            createClinicRequiredDocument(data: $data) { id }
        }`,
        { data },
    );

const deleteRequiredDocMutation = (id: string) =>
    apiClient.graphql(
        `mutation DeleteClinicRequiredDocument($id: UUID!) {
            deleteClinicRequiredDocument(id: $id) { id }
        }`,
        { id },
    );

// -- Page --------------------------------------------------------------------

export const ClinicsPage = () => {
    const [clinics, setClinics] = useState<ClinicNode[]>([]);
    const [loading, setLoading] = useState(true);
    const [slideoutOpen, setSlideoutOpen] = useState(false);
    const [editTarget, setEditTarget] = useState<ClinicNode | null>(null);
    const [formValues, setFormValues] = useState<ClinicFormValues>(emptyClinicFormValues);
    const [isSaving, setIsSaving] = useState(false);

    const fetchClinics = useCallback(async () => {
        try {
            const data = await apiClient.graphql<{ clinics: { edges: { node: ClinicNode }[] } }>(
                CLINICS_QUERY,
            );
            setClinics(data.clinics.edges.map((e) => e.node));
        } catch {
            // toast already shown by apiClient
        } finally {
            setLoading(false);
        }
    }, []);

    useEffect(() => {
        fetchClinics();
    }, [fetchClinics]);

    const handleAdd = () => {
        setEditTarget(null);
        setFormValues(emptyClinicFormValues());
        setSlideoutOpen(true);
    };

    const handleEdit = (clinic: ClinicNode) => {
        setEditTarget(clinic);
        setFormValues(toFormValues(clinic));
        setSlideoutOpen(true);
    };

    const handleSave = async (close: () => void) => {
        if (!formValues.clinicName.trim()) {
            notify.error('Clinic name is required');
            return;
        }
        setIsSaving(true);
        try {
            const coreInput = clinicCoreToGraphQLInput(formValues);

            // 1. Upsert the clinic itself.
            let clinicId: string;
            if (editTarget) {
                await updateClinicMutation(editTarget.id, coreInput);
                clinicId = editTarget.id;
            } else {
                const res = await createClinicMutation(coreInput);
                clinicId = res.createClinic.id;
                notify.success('Clinic added', `${formValues.clinicName} has been added.`);
                markSetupStepComplete('clinics').catch(() => {});
            }

            // 2. Holidays — delete-all-recreate. Simple, always correct.
            if (editTarget?.holidays?.edges?.length) {
                await Promise.all(
                    editTarget.holidays.edges.map((e) => deleteHolidayMutation(e.node.id)),
                );
            }
            if (formValues.holidays.length > 0) {
                const holidayInputs = holidayInputsFromForm(formValues, clinicId);
                await Promise.all(holidayInputs.map((data) => createHolidayMutation(data)));
            }

            // 3. Required docs — delete-all-recreate for symmetry.
            if (editTarget?.clinicRequiredDocuments?.edges?.length) {
                await Promise.all(
                    editTarget.clinicRequiredDocuments.edges.map((e) =>
                        deleteRequiredDocMutation(e.node.id),
                    ),
                );
            }
            if (formValues.requiredDocumentTypes.length > 0) {
                const docInputs = requiredDocInputsFromForm(formValues, clinicId);
                await Promise.all(docInputs.map((data) => createRequiredDocMutation(data)));
            }

            if (editTarget) {
                notify.success('Clinic updated', `${formValues.clinicName} has been updated.`);
            }
            await fetchClinics();
            close();
        } catch (err) {
            console.error('[clinics] save failed', err);
        } finally {
            setIsSaving(false);
        }
    };

    const activeCount = useMemo(
        () => clinics.filter((c) => c.status === 'ACTIVE').length,
        [clinics],
    );

    return (
        <div className="flex flex-1 flex-col overflow-hidden">
            <TopBar title="Clinics" subtitle="Manage hospital branches and visiting hours" />

            <div className="flex-1 overflow-y-auto p-6">
                <TableCard.Root size="sm">
                    <TableCard.Header
                        title="Clinic branches"
                        badge={clinics.length}
                        description={`${activeCount} active`}
                        contentTrailing={
                            <Button
                                size="sm"
                                color="primary"
                                iconLeading={({ className }: { className?: string }) => (
                                    <FontAwesomeIcon icon={faPlus} className={className} />
                                )}
                                onClick={handleAdd}
                            >
                                Add clinic
                            </Button>
                        }
                    />
                    {loading ? (
                        <div className="flex items-center justify-center py-12">
                            <p className="text-sm text-tertiary">Loading clinics...</p>
                        </div>
                    ) : clinics.length === 0 ? (
                        <div className="flex flex-col items-center justify-center gap-3 py-16">
                            <div className="flex size-12 items-center justify-center rounded-full bg-brand-secondary">
                                <FontAwesomeIcon icon={faBuilding} className="size-5 text-fg-brand-primary" />
                            </div>
                            <p className="text-sm font-semibold text-primary">No clinics yet</p>
                            <p className="max-w-xs text-center text-xs text-tertiary">
                                Add your first clinic branch to start booking appointments and assigning doctors.
                            </p>
                            <Button size="sm" color="primary" onClick={handleAdd}>
                                Add clinic
                            </Button>
                        </div>
                    ) : (
                        <Table>
                            <Table.Header>
                                <Table.Head label="NAME" isRowHeader />
                                <Table.Head label="ADDRESS" />
                                <Table.Head label="CONTACT" />
                                <Table.Head label="HOURS" />
                                <Table.Head label="STATUS" />
                                <Table.Head label="" />
                            </Table.Header>
                            <Table.Body items={clinics}>
                                {(clinic) => {
                                    const addressLine = [
                                        clinic.addressCustom?.addressStreet1,
                                        clinic.addressCustom?.addressCity,
                                    ]
                                        .filter(Boolean)
                                        .join(', ');
                                    const status = clinic.status ?? 'ACTIVE';
                                    const dayLabel = formatDaySelection(toDaySelection(clinic));
                                    const hoursLabel =
                                        clinic.opensAt && clinic.closesAt
                                            ? `${formatTimeLabel(clinic.opensAt)}–${formatTimeLabel(clinic.closesAt)}`
                                            : 'Not set';
                                    return (
                                        <Table.Row id={clinic.id}>
                                            <Table.Cell>
                                                <span className="text-sm font-medium text-primary">
                                                    {clinic.clinicName ?? 'Unnamed clinic'}
                                                </span>
                                            </Table.Cell>
                                            <Table.Cell>
                                                <span className="text-sm text-tertiary">{addressLine || '—'}</span>
                                            </Table.Cell>
                                            <Table.Cell>
                                                <div className="flex flex-col">
                                                    <span className="text-sm text-primary">
                                                        {clinic.phone?.primaryPhoneNumber ?? '—'}
                                                    </span>
                                                    <span className="text-xs text-tertiary">
                                                        {clinic.email?.primaryEmail ?? ''}
                                                    </span>
                                                </div>
                                            </Table.Cell>
                                            <Table.Cell>
                                                <div className="flex flex-col">
                                                    <span className="text-sm text-primary">{dayLabel}</span>
                                                    <span className="text-xs text-tertiary">{hoursLabel}</span>
                                                </div>
                                            </Table.Cell>
                                            <Table.Cell>
                                                <Badge size="sm" color={statusColor[status]} type="pill-color">
                                                    {statusLabel[status]}
                                                </Badge>
                                            </Table.Cell>
                                            <Table.Cell>
                                                <Button
                                                    size="sm"
                                                    color="secondary"
                                                    iconLeading={({ className }: { className?: string }) => (
                                                        <FontAwesomeIcon icon={faPenToSquare} className={className} />
                                                    )}
                                                    onClick={() => handleEdit(clinic)}
                                                >
                                                    Edit
                                                </Button>
                                            </Table.Cell>
                                        </Table.Row>
                                    );
                                }}
                            </Table.Body>
                        </Table>
                    )}
                </TableCard.Root>
            </div>

            <SlideoutMenu isOpen={slideoutOpen} onOpenChange={setSlideoutOpen} isDismissable>
                {({ close }) => (
                    <>
                        <SlideoutMenu.Header onClose={close}>
                            <div className="flex items-center gap-3 pr-8">
                                <div className="flex size-10 items-center justify-center rounded-lg bg-brand-secondary">
                                    <FontAwesomeIcon icon={faBuilding} className="size-5 text-fg-brand-primary" />
                                </div>
                                <div>
                                    <h2 className="text-lg font-semibold text-primary">
                                        {editTarget ? 'Edit clinic' : 'Add clinic'}
                                    </h2>
                                    <p className="text-sm text-tertiary">
                                        {editTarget
                                            ? 'Update branch details, hours, and policy'
                                            : 'Add a new hospital branch'}
                                    </p>
                                </div>
                            </div>
                        </SlideoutMenu.Header>

                        <SlideoutMenu.Content>
                            <ClinicForm value={formValues} onChange={setFormValues} />
                        </SlideoutMenu.Content>

                        <SlideoutMenu.Footer>
                            <div className="flex items-center justify-end gap-3">
                                <Button size="md" color="secondary" onClick={close}>
                                    Cancel
                                </Button>
                                <Button
                                    size="md"
                                    color="primary"
                                    isLoading={isSaving}
                                    showTextWhileLoading
                                    onClick={() => handleSave(close)}
                                >
                                    {isSaving ? 'Saving...' : editTarget ? 'Save changes' : 'Add clinic'}
                                </Button>
                            </div>
                        </SlideoutMenu.Footer>
                    </>
                )}
            </SlideoutMenu>
        </div>
    );
};
