import { Head, Link, router } from '@inertiajs/react';
import { useState } from 'react';

import HeadingSmall from '@/components/heading-small';
import { SortableHeader } from '@/components/sortable-header';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { useUserTimezone } from '@/hooks/use-user-timezone';
import AppLayout from '@/layouts/app-layout';
import { formatDateTime } from '@/lib/format-date';
import { index, index as logsIndex } from '@/actions/App/Http/Controllers/Admin/PublicApiRequestLogController';
import { index as enrollmentRequestsIndex } from '@/actions/App/Http/Controllers/Admin/EnrollmentRequestController';
import { index as creditCheckRequestsIndex } from '@/actions/App/Http/Controllers/Admin/CreditCheckRequestController';

type ApiLog = {
    id: number;
    method: string;
    path: string;
    ip_address: string | null;
    request_headers: Record<string, unknown> | null;
    request_body: Record<string, unknown> | null;
    response_status: number | null;
    response_body: Record<string, unknown> | null;
    loggable_type: string | null;
    loggable_id: number | null;
    duration_ms: number | null;
    created_at: string;
};

type PaginatedLogs = {
    data: ApiLog[];
    current_page: number;
    last_page: number;
    per_page: number;
    total: number;
    next_page_url: string | null;
    prev_page_url: string | null;
    links: { url: string | null; label: string; active: boolean }[];
};

type Filters = {
    id: string;
    search: string;
    method: string;
    status: string;
    loggable_type: string;
    sort_by: string;
    sort_direction: string;
};

type Props = {
    logs: PaginatedLogs;
    filters: Filters;
};

function MethodBadge({ method }: { method: string }) {
    const colors: Record<string, string> = {
        GET: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300',
        POST: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300',
        PUT: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300',
        PATCH: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300',
        DELETE: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300',
    };

    return (
        <span className={`inline-block rounded px-1.5 py-0.5 font-mono text-xs font-semibold ${colors[method] ?? 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400'}`}>
            {method}
        </span>
    );
}

function StatusBadge({ status }: { status: number | null }) {
    if (status === null) return <span className="text-zinc-400">—</span>;

    let colorClass = 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400';
    if (status >= 200 && status < 300) {
        colorClass = 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300';
    } else if (status >= 400 && status < 500) {
        colorClass = 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300';
    } else if (status >= 500) {
        colorClass = 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300';
    }

    return (
        <span className={`inline-block rounded-full px-2 py-0.5 font-mono text-xs font-medium ${colorClass}`}>
            {status}
        </span>
    );
}

function loggableLabel(loggableType: string | null): string {
    if (!loggableType) return '—';
    if (loggableType.includes('EnrollmentRequest')) return 'Enrollment';
    if (loggableType.includes('CreditCheckRequest')) return 'Credit Check';
    return loggableType.split('\\').pop() ?? loggableType;
}

function LoggableLink({ log }: { log: ApiLog }) {
    if (!log.loggable_type || !log.loggable_id) {
        return <span className="text-zinc-400 text-xs">—</span>;
    }

    const label = loggableLabel(log.loggable_type);

    let href: string;
    if (log.loggable_type.includes('EnrollmentRequest')) {
        href = enrollmentRequestsIndex.url({ query: { id: String(log.loggable_id) } });
    } else if (log.loggable_type.includes('CreditCheckRequest')) {
        href = creditCheckRequestsIndex.url({ query: { enrollment_request_id: String(log.loggable_id) } });
    } else {
        return <span className="text-xs text-zinc-500">{label} #{log.loggable_id}</span>;
    }

    return (
        <Link
            href={href}
            className="inline-flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800 hover:underline dark:text-blue-400 dark:hover:text-blue-300"
            onClick={(e) => e.stopPropagation()}
        >
            {label} #{log.loggable_id} →
        </Link>
    );
}

function JsonBlock({ data, label }: { data: Record<string, unknown> | null; label: string }) {
    const [raw, setRaw] = useState(false);

    if (!data || Object.keys(data).length === 0) {
        return (
            <div>
                <h4 className="mb-1 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{label}</h4>
                <span className="text-sm text-zinc-400">Empty</span>
            </div>
        );
    }

    return (
        <div>
            <div className="mb-1 flex items-center justify-between">
                <h4 className="text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{label}</h4>
                <button
                    type="button"
                    onClick={() => setRaw((v) => !v)}
                    className="rounded border border-zinc-200 bg-white px-2 py-0.5 text-xs text-zinc-600 hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800"
                >
                    {raw ? 'Formatted' : 'Raw JSON'}
                </button>
            </div>
            {raw ? (
                <pre className="overflow-x-auto rounded-md bg-zinc-50 p-3 font-mono text-xs text-zinc-800 dark:bg-zinc-900 dark:text-zinc-200">
                    {JSON.stringify(data, null, 2)}
                </pre>
            ) : (
                <dl className="divide-y divide-zinc-100 dark:divide-zinc-800">
                    {Object.entries(data).map(([key, value]) => (
                        <div key={key} className="grid grid-cols-[180px_1fr] gap-2 py-1.5 text-sm">
                            <dt className="truncate font-medium text-zinc-500 dark:text-zinc-400">{key}</dt>
                            <dd className="break-all text-zinc-900 dark:text-zinc-100">
                                {value === null || value === undefined
                                    ? <span className="text-zinc-400">—</span>
                                    : typeof value === 'object'
                                        ? <pre className="whitespace-pre-wrap font-mono text-xs">{JSON.stringify(value, null, 2)}</pre>
                                        : String(value)}
                            </dd>
                        </div>
                    ))}
                </dl>
            )}
        </div>
    );
}

function ApiLogDetailModal({
    log,
    open,
    onOpenChange,
}: {
    log: ApiLog | null;
    open: boolean;
    onOpenChange: (open: boolean) => void;
}) {
    const timezone = useUserTimezone();

    if (!log) return null;

    return (
        <Dialog open={open} onOpenChange={onOpenChange}>
            <DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto">
                <DialogHeader>
                    <DialogTitle>API Log #{log.id}</DialogTitle>
                </DialogHeader>

                <div className="space-y-6 pt-2">
                    {/* Meta */}
                    <dl className="divide-y divide-zinc-100 dark:divide-zinc-800">
                        {([
                            ['Method', <MethodBadge method={log.method} />],
                            ['Path', <span className="font-mono text-xs">{log.path}</span>],
                            ['IP Address', log.ip_address ?? '—'],
                            ['Response Status', <StatusBadge status={log.response_status} />],
                            ['Duration', log.duration_ms !== null ? `${log.duration_ms}ms` : '—'],
                            ['Linked Record', <LoggableLink log={log} />],
                            ['Received', formatDateTime(log.created_at, timezone)],
                        ] as [string, React.ReactNode][]).map(([label, value]) => (
                            <div key={label} className="grid grid-cols-[160px_1fr] gap-2 py-2 text-sm">
                                <dt className="font-medium text-zinc-500 dark:text-zinc-400">{label}</dt>
                                <dd className="text-zinc-900 dark:text-zinc-100">{value}</dd>
                            </div>
                        ))}
                    </dl>

                    <JsonBlock data={log.request_headers} label="Request Headers" />
                    <JsonBlock data={log.request_body} label="Request Body" />
                    <JsonBlock data={log.response_body} label="Response Body" />
                </div>
            </DialogContent>
        </Dialog>
    );
}

const breadcrumbs = [{ title: 'API Logs', href: index.url() }];

const selectClasses =
    'rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm shadow-sm dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100';

export default function PublicApiRequestLogs({ logs, filters }: Props) {
    const timezone = useUserTimezone();
    const [search, setSearch] = useState(filters.search);
    const [selectedLog, setSelectedLog] = useState<ApiLog | null>(null);

    const sortColumn = filters.sort_by || 'created_at';
    const sortDirection = (filters.sort_direction || 'desc') as 'asc' | 'desc';

    function toggleSort(column: string) {
        if (column === sortColumn) {
            applyFilters({ sort_by: column, sort_direction: sortDirection === 'asc' ? 'desc' : 'asc' });
        } else {
            applyFilters({ sort_by: column, sort_direction: 'desc' });
        }
    }

    function applyFilters(overrides: Partial<Filters> = {}) {
        const merged = { ...filters, ...overrides };
        router.get(
            logsIndex.url(),
            Object.fromEntries(Object.entries(merged).filter(([, v]) => v !== '')),
            { preserveState: true, replace: true },
        );
    }

    const hasActiveFilters = filters.id || filters.search || filters.method || filters.status || filters.loggable_type;

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title="API Logs" />

            <h1 className="sr-only">Public API Request Logs</h1>

            <div className="space-y-6">
                <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
                    <HeadingSmall
                        title="API Logs"
                        description="Inbound public API requests and their responses"
                    />

                    <div className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-end">
                        <div className="flex flex-col gap-1 sm:min-w-52">
                            <label className="text-xs font-medium text-zinc-700 dark:text-zinc-300">Search</label>
                            <input
                                type="text"
                                value={search}
                                onChange={(e) => setSearch(e.target.value)}
                                onKeyDown={(e) => { if (e.key === 'Enter') { applyFilters({ search }); } }}
                                placeholder="Path or IP address…"
                                className={selectClasses}
                            />
                        </div>

                        <div className="flex flex-col gap-1">
                            <label className="text-xs font-medium text-zinc-700 dark:text-zinc-300">Method</label>
                            <select
                                value={filters.method}
                                onChange={(e) => applyFilters({ method: e.target.value })}
                                className={selectClasses}
                            >
                                <option value="">All</option>
                                <option value="GET">GET</option>
                                <option value="POST">POST</option>
                            </select>
                        </div>

                        <div className="flex flex-col gap-1">
                            <label className="text-xs font-medium text-zinc-700 dark:text-zinc-300">Status</label>
                            <select
                                value={filters.status}
                                onChange={(e) => applyFilters({ status: e.target.value })}
                                className={selectClasses}
                            >
                                <option value="">All</option>
                                <option value="2xx">2xx Success</option>
                                <option value="4xx">4xx Client Error</option>
                                <option value="5xx">5xx Server Error</option>
                            </select>
                        </div>

                        <div className="flex flex-col gap-1">
                            <label className="text-xs font-medium text-zinc-700 dark:text-zinc-300">Linked Record</label>
                            <select
                                value={filters.loggable_type}
                                onChange={(e) => applyFilters({ loggable_type: e.target.value })}
                                className={selectClasses}
                            >
                                <option value="">All</option>
                                <option value="App\Models\EnrollmentRequest">Enrollment</option>
                                <option value="App\Models\CreditCheckRequest">Credit Check</option>
                            </select>
                        </div>

                        <button
                            type="button"
                            onClick={() => applyFilters({ search })}
                            className="rounded-md bg-zinc-900 px-3 py-2 text-sm font-medium text-white dark:bg-zinc-100 dark:text-zinc-900"
                        >
                            Search
                        </button>

                        {hasActiveFilters && (
                            <button
                                type="button"
                                onClick={() => {
                                    setSearch('');
                                    applyFilters({ id: '', search: '', method: '', status: '', loggable_type: '' });
                                }}
                                className="rounded-md px-3 py-2 text-sm font-medium text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100"
                            >
                                Clear
                            </button>
                        )}
                    </div>
                </div>

                <div className="overflow-hidden rounded-md border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950">
                    <div className="overflow-x-auto">
                        <table className="w-full text-left text-sm">
                            <thead className="border-b border-zinc-200 text-xs uppercase text-zinc-600 dark:border-zinc-800 dark:text-zinc-400">
                                <tr>
                                    <SortableHeader label="ID" column="id" sortColumn={sortColumn} sortDirection={sortDirection} onSort={toggleSort} />
                                    <th className="px-4 py-3">Method</th>
                                    <th className="px-4 py-3">Path</th>
                                    <th className="px-4 py-3">IP Address</th>
                                    <SortableHeader label="Status" column="response_status" sortColumn={sortColumn} sortDirection={sortDirection} onSort={toggleSort} />
                                    <SortableHeader label="Duration" column="duration_ms" sortColumn={sortColumn} sortDirection={sortDirection} onSort={toggleSort} />
                                    <th className="px-4 py-3">Linked Record</th>
                                    <SortableHeader label="Received" column="created_at" sortColumn={sortColumn} sortDirection={sortDirection} onSort={toggleSort} />
                                </tr>
                            </thead>
                            <tbody className="divide-y divide-zinc-200 dark:divide-zinc-800">
                                {logs.data.map((log) => (
                                    <tr
                                        key={log.id}
                                        onClick={() => setSelectedLog(log)}
                                        className="cursor-pointer hover:bg-zinc-50 dark:hover:bg-zinc-900"
                                    >
                                        <td className="px-4 py-3 font-medium">{log.id}</td>
                                        <td className="px-4 py-3">
                                            <MethodBadge method={log.method} />
                                        </td>
                                        <td className="px-4 py-3 font-mono text-xs text-zinc-700 dark:text-zinc-300 max-w-xs truncate">
                                            {log.path}
                                        </td>
                                        <td className="px-4 py-3 text-zinc-600 dark:text-zinc-400">{log.ip_address ?? '—'}</td>
                                        <td className="px-4 py-3">
                                            <StatusBadge status={log.response_status} />
                                        </td>
                                        <td className="px-4 py-3 text-zinc-600 dark:text-zinc-400">
                                            {log.duration_ms !== null ? `${log.duration_ms}ms` : '—'}
                                        </td>
                                        <td className="px-4 py-3" onClick={(e) => e.stopPropagation()}>
                                            <LoggableLink log={log} />
                                        </td>
                                        <td className="px-4 py-3 text-zinc-600 dark:text-zinc-400">
                                            {formatDateTime(log.created_at, timezone)}
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>

                    {logs.data.length === 0 && (
                        <div className="p-4 text-sm text-zinc-600 dark:text-zinc-400">
                            No API logs yet.
                        </div>
                    )}
                </div>

                {logs.last_page > 1 && (
                    <div className="flex items-center justify-between text-sm">
                        <span className="text-zinc-500 dark:text-zinc-400">
                            Showing{' '}
                            {(logs.current_page - 1) * logs.per_page + 1}
                            –
                            {Math.min(logs.current_page * logs.per_page, logs.total)}{' '}
                            of {logs.total}
                        </span>
                        <div className="flex gap-1">
                            {logs.links.map((link) => (
                                <Link
                                    key={link.label}
                                    href={link.url ?? '#'}
                                    preserveState
                                    className={`rounded-md px-3 py-1.5 text-xs ${
                                        link.active
                                            ? 'bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900'
                                            : link.url
                                              ? 'text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800'
                                              : 'pointer-events-none text-zinc-300 dark:text-zinc-700'
                                    }`}
                                    dangerouslySetInnerHTML={{ __html: link.label }}
                                />
                            ))}
                        </div>
                    </div>
                )}
            </div>

            <ApiLogDetailModal
                log={selectedLog}
                open={selectedLog !== null}
                onOpenChange={(open) => { if (!open) setSelectedLog(null); }}
            />
        </AppLayout>
    );
}
