import { Head, router, useForm } from '@inertiajs/react';
import {
    CheckCircle2,
    Download,
    FileSpreadsheet,
    Link2,
    RefreshCw,
    ShieldAlert,
    Unplug,
} from 'lucide-react';
import { FormField, NativeSelect } from '@/components/crm/native-select';
import { Callout, SectionCard } from '@/components/crm/page-header';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import {
    check as checkSheet,
    commit as importSheet,
    destroy as disconnectSheet,
    update as updateSheet,
} from '@/routes/sheet-import/source';
import type {
    SheetImportProps,
    SheetImportReport,
    SheetImportSource,
    SheetImportSourceSettings,
    SheetImportStats,
} from '@/types';

/**
 * Settings -> Sheet import.
 *
 * Three sheets can be read automatically: the client list, the job sheet and
 * the installer register. Each is its own card, because each is its own
 * spreadsheet with its own link, its own cadence and its own report — connecting
 * one must never disturb another.
 *
 * A card shows what the sheet holds before anything is written, then offers to
 * write it. Nothing is ever written back to a sheet.
 */
export default function SheetImportSettings({
    sources,
    schedules,
}: SheetImportProps) {
    const connected = sources.filter((source) => source.url !== null).length;

    return (
        <>
            <Head title="Sheet import" />

            <Callout
                tone={connected > 0 ? 'success' : 'info'}
                icon={connected > 0 ? CheckCircle2 : Link2}
                title={
                    connected > 0
                        ? `${connected} of ${sources.length} sheets connected`
                        : 'Connect a Google Sheet to each list you want kept in step'
                }
            >
                Paste the link you would send a colleague — the one with{' '}
                <span className="font-mono text-xs">#gid=</span> on the end — and
                the system reads it, says what it found, and offers to import it.
                The schedule reads the sheet and writes a report; it only imports
                when the second switch is on. Nothing is ever written back to the
                sheet.
            </Callout>

            {sources.map((source) => (
                <SourceCard
                    key={source.source}
                    source={source}
                    schedules={schedules}
                />
            ))}
        </>
    );
}

/**
 * One sheet: its link, its switches and what the last read found.
 */
function SourceCard({
    source,
    schedules,
}: {
    source: SheetImportSourceSettings;
    schedules: Array<{ value: string; label: string }>;
}) {
    const form = useForm({
        url: source.url ?? '',
        sync: source.enabled,
        cron: source.cron,
        commit: source.commits,
    });

    const isConnected = source.url !== null;

    const submit = () => {
        form.post(updateSheet(source.source).url, { preserveScroll: true });
    };

    const checkNow = () => {
        router.post(
            checkSheet(source.source).url,
            {},
            { preserveScroll: true },
        );
    };

    const importNow = () => {
        const ready = source.preview?.stats.ready ?? 0;

        if (
            !window.confirm(
                `Import ${ready} ${noun(source.source, true)} from the sheet? A database backup is taken and verified first.`,
            )
        ) {
            return;
        }

        router.post(
            importSheet(source.source).url,
            {},
            { preserveScroll: true },
        );
    };

    const disconnect = () => {
        if (
            !window.confirm(
                `Disconnect the ${source.label.toLowerCase()} sheet and switch its sync off? Nothing already imported is removed.`,
            )
        ) {
            return;
        }

        router.delete(disconnectSheet(source.source).url, {
            preserveScroll: true,
        });
    };

    return (
        <SectionCard
            title={`${source.label} sheet`}
            description={source.description}
            icon={FileSpreadsheet}
            tone={isConnected ? 'emerald' : 'solar'}
        >
            <FormField
                label="Google Sheet link"
                htmlFor={`url-${source.source}`}
                error={form.errors.url as string | undefined}
                hint={
                    source.source === 'installers'
                        ? 'The whole workbook is read, one tab per state. Share → Anyone with the link → Viewer'
                        : 'The tab in the link is the tab read. Share → Anyone with the link → Viewer'
                }
                required
            >
                <Input
                    id={`url-${source.source}`}
                    value={form.data.url}
                    placeholder="https://docs.google.com/spreadsheets/d/…/edit#gid=1895975164"
                    onChange={(event) =>
                        form.setData('url', event.target.value)
                    }
                />
            </FormField>

            <div className="mt-3">
                <Callout
                    tone="warning"
                    icon={ShieldAlert}
                    title="This link can be read by anyone who has it"
                >
                    Sharing by link is what lets the import read the sheet
                    without a Google sign-in. It also means anybody holding that
                    link can read everything in it — so only connect a sheet
                    whose contents are safe to share, and unshare it when the
                    import is done.
                </Callout>
            </div>

            <div className="mt-4 flex flex-wrap items-center gap-2 border-t border-hairline pt-4">
                <Button
                    type="button"
                    onClick={submit}
                    disabled={form.processing}
                >
                    {form.processing && <Spinner />}
                    {isConnected ? 'Save and check' : 'Connect and check'}
                </Button>

                {isConnected ? (
                    <>
                        <Button
                            type="button"
                            variant="outline"
                            onClick={checkNow}
                        >
                            <RefreshCw />
                            Check now
                        </Button>
                        <Button
                            type="button"
                            variant="ghost"
                            onClick={disconnect}
                        >
                            <Unplug />
                            Disconnect
                        </Button>
                    </>
                ) : null}
            </div>

            <div className="mt-5 grid gap-4 border-t border-hairline pt-4 sm:grid-cols-[1fr_auto] sm:items-start">
                <div className="space-y-3">
                    <div className="flex items-start gap-3">
                        <Checkbox
                            id={`sync-${source.source}`}
                            className="mt-0.5"
                            checked={form.data.sync}
                            onCheckedChange={(checked) =>
                                form.setData('sync', checked === true)
                            }
                        />
                        <div className="min-w-0">
                            <Label htmlFor={`sync-${source.source}`}>
                                Read the sheet on a schedule
                            </Label>
                            <p className="mt-1 text-xs leading-relaxed text-muted-foreground">
                                Each run writes a report of what the sheet now
                                holds. Leave this off and the sheet is only read
                                when you press Check now.
                            </p>
                        </div>
                    </div>

                    <div className="flex items-start gap-3">
                        <Checkbox
                            id={`commit-${source.source}`}
                            className="mt-0.5"
                            checked={form.data.commit}
                            onCheckedChange={(checked) =>
                                form.setData('commit', checked === true)
                            }
                        />
                        <div className="min-w-0">
                            <Label htmlFor={`commit-${source.source}`}>
                                Import new rows automatically
                            </Label>
                            <p className="mt-1 text-xs leading-relaxed text-muted-foreground">
                                A scheduled run writes what it finds, after a
                                verified backup. Leaving this off keeps the
                                schedule read-only until you press Import.
                            </p>
                        </div>
                    </div>
                </div>

                <div className="w-full sm:w-44">
                    <FormField
                        label="How often"
                        htmlFor={`cron-${source.source}`}
                        error={form.errors.cron as string | undefined}
                    >
                        <NativeSelect
                            id={`cron-${source.source}`}
                            value={form.data.cron}
                            onChange={(event) =>
                                form.setData('cron', event.target.value)
                            }
                        >
                            {schedules.map((option) => (
                                <option key={option.value} value={option.value}>
                                    {option.label}
                                </option>
                            ))}
                        </NativeSelect>
                    </FormField>

                    <Button
                        type="button"
                        variant="outline"
                        size="sm"
                        className="mt-3 w-full"
                        onClick={submit}
                        disabled={form.processing}
                    >
                        Save settings
                    </Button>
                </div>
            </div>

            {source.preview ? (
                <div className="mt-5 border-t border-hairline pt-4">
                    <Report
                        title="What the sheet holds"
                        source={source.source}
                        report={source.preview}
                        onImport={isConnected ? importNow : undefined}
                    />
                </div>
            ) : null}

            {source.import ? (
                <div className="mt-4">
                    <Report
                        title="Last import"
                        source={source.source}
                        report={source.import}
                    />
                </div>
            ) : null}

            {source.last_run ? (
                <p className="mt-3 text-xs text-muted-foreground">
                    Last run {new Date(source.last_run.at).toLocaleString()}
                    {source.last_run.committed ? ' (imported)' : ' (report only)'}
                </p>
            ) : null}

            <p className="mt-3 font-mono text-[11px] text-muted-foreground/70">
                {source.command}
            </p>
        </SectionCard>
    );
}

/**
 * A report is the same shape whether it came from a dry run or a real import,
 * which is what lets the card look the same before and after.
 */
function Report({
    title,
    source,
    report,
    onImport,
}: {
    title: string;
    source: SheetImportSource;
    report: SheetImportReport;
    onImport?: () => void;
}) {
    const stats = report.stats;
    const ready = stats.ready ?? 0;

    return (
        <div>
            <div className="flex flex-wrap items-start justify-between gap-2">
                <div>
                    <p className="text-sm font-semibold">{title}</p>
                    {report.imported_at ? (
                        <p className="text-xs text-muted-foreground">
                            Read{' '}
                            {new Date(report.imported_at).toLocaleString()}
                            {report.sheet ? ` from ${report.sheet}` : ''}
                            {report.committed ? '' : ' · nothing written'}
                        </p>
                    ) : null}
                </div>

                {onImport && ready > 0 ? (
                    <Button type="button" size="sm" onClick={onImport}>
                        <Download />
                        Import {ready} {noun(source, ready !== 1)}
                    </Button>
                ) : null}
            </div>

            <div className="mt-3 flex flex-wrap gap-2">
                {statChips(source, stats).map((chip) => (
                    <Stat
                        key={chip.label}
                        label={chip.label}
                        value={chip.value}
                        tone={chip.tone}
                    />
                ))}
            </div>

            {report.rows.length > 0 ? (
                <div className="mt-3 overflow-x-auto">
                    <table className="w-full text-sm">
                        <thead>
                            <tr className="text-left text-xs text-muted-foreground">
                                <th className="py-1 pr-3 font-medium">Row</th>
                                {report.rows.some((row) => row.sheet) ? (
                                    <th className="py-1 pr-3 font-medium">Tab</th>
                                ) : null}
                                <th className="py-1 pr-3 font-medium">Name</th>
                                <th className="py-1 pr-3 font-medium">
                                    Address
                                </th>
                                <th className="py-1 font-medium">Outcome</th>
                            </tr>
                        </thead>
                        <tbody>
                            {report.rows.map((row) => (
                                <tr
                                    key={`${row.sheet}-${row.row}-${row.name}`}
                                    className="border-t border-hairline align-top"
                                >
                                    <td className="py-1.5 pr-3 text-muted-foreground tabular-nums">
                                        {row.row}
                                    </td>
                                    {report.rows.some((each) => each.sheet) ? (
                                        <td className="py-1.5 pr-3 text-muted-foreground">
                                            {row.sheet ?? '—'}
                                        </td>
                                    ) : null}
                                    <td className="py-1.5 pr-3">
                                        {row.name ?? '—'}
                                    </td>
                                    <td className="max-w-60 truncate py-1.5 pr-3 text-muted-foreground">
                                        {row.address ?? '—'}
                                    </td>
                                    <td className="py-1.5">
                                        <Outcome
                                            outcome={row.outcome}
                                            reason={row.reason}
                                        />
                                    </td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </div>
            ) : (
                <p className="mt-3 text-xs text-muted-foreground">
                    The sheet has no rows the importer could read.
                </p>
            )}

            {report.row_count > report.rows.length ? (
                <p className="mt-2 text-xs text-muted-foreground">
                    Showing the first {report.rows.length} of {report.row_count}{' '}
                    rows.
                </p>
            ) : null}
        </div>
    );
}

/**
 * The counts worth showing for one source. A job sheet has no "updated" count
 * and a client sheet has no sites, so the chips follow the report rather than
 * a fixed list.
 */
function statChips(source: SheetImportSource, stats: SheetImportStats) {
    const chips: Array<{ label: string; value: number; tone?: 'good' }> = [];
    const add = (key: keyof SheetImportStats, label: string, tone?: 'good') => {
        const value = stats[key];

        if (typeof value === 'number') {
            chips.push({ label, value, tone });
        }
    };

    add('rows', 'Rows read');
    add('ready', readyLabel(source), 'good');
    add('updates', 'To update');
    add('duplicates', 'Duplicates');
    add('missing_name', 'No name');
    add('missing_contact', 'No contact');
    add('missing_address', 'No address');
    add('invalid_address', 'Address unusable');
    add('inactive', 'Not used any more');
    add('new_customers', 'New customers');
    add('existing_customers', 'Existing customers');
    add('new_sites', 'New sites');

    return chips;
}

function readyLabel(source: SheetImportSource): string {
    return {
        clients: 'Clients to create',
        jobs: 'Jobs to create',
        installers: 'Installers to add',
    }[source];
}

function noun(source: SheetImportSource, plural: boolean): string {
    const word = {
        clients: 'client',
        jobs: 'job',
        installers: 'installer',
    }[source];

    return plural ? `${word}s` : word;
}

function Stat({
    label,
    value,
    tone,
}: {
    label: string;
    value: number;
    tone?: 'good';
}) {
    return (
        <div
            className={cn(
                'rounded-lg px-3 py-2 ring-1 ring-hairline',
                tone === 'good' && 'bg-emerald-500/10 ring-emerald-500/25',
            )}
        >
            <p className="text-xs text-muted-foreground">{label}</p>
            <p className="text-base font-semibold tabular-nums">{value}</p>
        </div>
    );
}

function Outcome({
    outcome,
    reason,
}: {
    outcome: string | null;
    reason: string | null;
}) {
    const label =
        {
            create: 'Will import',
            update: 'Will update',
            duplicate: 'Duplicate',
            missing_name: 'No name',
            missing_contact: 'No contact',
            missing_address: 'No address',
            invalid_address: 'Address not usable',
        }[outcome ?? ''] ?? 'Skipped';

    return (
        <div>
            <span
                className={cn(
                    'text-xs font-medium',
                    outcome === 'create' || outcome === 'update'
                        ? 'text-emerald-700 dark:text-emerald-300'
                        : 'text-muted-foreground',
                )}
            >
                {label}
            </span>
            {reason ? (
                <p className="mt-0.5 text-xs text-muted-foreground">{reason}</p>
            ) : null}
        </div>
    );
}

SheetImportSettings.layout = {
    breadcrumbs: [
        {
            title: 'Sheet import',
            href: '/settings/sheet-import',
        },
    ],
};
