import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
    KeyRound,
    Plus,
    Copy,
    Check,
    Trash2,
    ShieldCheck,
    AlertTriangle,
    Loader2,
} from 'lucide-react';
import { getErrorMessage } from '@nexuscrm/host-sdk';
import { Button } from '@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui';
import { Badge } from '@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui';
import { ConfirmationModal } from '@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui';
import { useSuccessToast, useErrorToast } from '@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui';
import {
    apiTokensAPI,
    API_TOKEN_SCOPES,
    type ApiToken,
    type ApiTokenScope,
} from '../infrastructure/api/apiTokens';

interface ExpirationOption {
    label: string;
    value: number; // days; 0 = never
}

const EXPIRATION_OPTIONS: ExpirationOption[] = [
    { label: '30 days', value: 30 },
    { label: '60 days', value: 60 },
    { label: '90 days', value: 90 },
    { label: 'No expiration', value: 0 },
];

const SCOPE_LABELS: Record<ApiTokenScope, string> = {
    'records.read': 'Read records',
    'records.write': 'Write records',
    'reports.read': 'Read reports',
    'analytics.write': 'Write analytics',
};

function formatDate(value: string | null): string {
    if (!value) return '—';
    const parsed = new Date(value);
    if (Number.isNaN(parsed.getTime())) return '—';
    return parsed.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
}

function scopeList(scopes: string | undefined | null): string[] {
    return (scopes ?? '').split(/\s+/).map(s => s.trim()).filter(Boolean);
}

// ─────────────────────────────────────────────────────────────────────────────
// Copy button
// ─────────────────────────────────────────────────────────────────────────────

const CopyButton: React.FC<{ value: string; label?: string }> = ({ value, label }) => {
    const [copied, setCopied] = useState(false);
    const onCopy = useCallback(async () => {
        try {
            await navigator.clipboard.writeText(value);
            setCopied(true);
            window.setTimeout(() => setCopied(false), 2000);
        } catch {
            setCopied(false);
        }
    }, [value]);
    return (
        <button
            type="button"
            onClick={onCopy}
            className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-lg border border-slate-300 bg-white text-slate-700 hover:bg-slate-50 transition-colors"
            aria-label={label ? `Copy ${label}` : 'Copy'}
        >
            {copied ? <Check size={15} className="text-green-600" /> : <Copy size={15} />}
            <span>{copied ? 'Copied' : 'Copy'}</span>
        </button>
    );
};

// ─────────────────────────────────────────────────────────────────────────────
// Create token form
// ─────────────────────────────────────────────────────────────────────────────

interface CreateTokenFormProps {
    creating: boolean;
    onCreate: (name: string, scopes: string, expiresInDays: number) => void;
}

const CreateTokenForm: React.FC<CreateTokenFormProps> = ({ creating, onCreate }) => {
    const [name, setName] = useState('');
    const [selectedScopes, setSelectedScopes] = useState<ApiTokenScope[]>(['records.read']);
    const [expiration, setExpiration] = useState<number>(90);

    const toggleScope = (scope: ApiTokenScope) => {
        setSelectedScopes(prev =>
            prev.includes(scope) ? prev.filter(s => s !== scope) : [...prev, scope],
        );
    };

    const canSubmit = name.trim().length > 0 && selectedScopes.length > 0 && !creating;

    const handleSubmit = (e: React.FormEvent) => {
        e.preventDefault();
        if (!canSubmit) return;
        onCreate(name.trim(), selectedScopes.join(' '), expiration);
        setName('');
        setSelectedScopes(['records.read']);
        setExpiration(90);
    };

    return (
        <form
            onSubmit={handleSubmit}
            className="bg-white border border-slate-200 rounded-xl shadow-sm p-6 space-y-5"
        >
            <div className="flex items-center gap-2">
                <Plus size={18} className="text-blue-600" />
                <h2 className="text-base font-semibold text-slate-900">Create a token</h2>
            </div>

            <div>
                <label htmlFor="token-name" className="block text-sm font-medium text-slate-700 mb-1.5">
                    Token name
                </label>
                <input
                    id="token-name"
                    type="text"
                    value={name}
                    onChange={e => setName(e.target.value)}
                    placeholder="e.g. Laptop CLI, Cursor MCP"
                    className="w-full px-3 py-2 text-sm rounded-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
                    maxLength={120}
                />
                <p className="mt-1 text-xs text-slate-500">A label to help you recognize where this token is used.</p>
            </div>

            <div>
                <span className="block text-sm font-medium text-slate-700 mb-2">Scopes</span>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
                    {API_TOKEN_SCOPES.map(scope => {
                        const checked = selectedScopes.includes(scope);
                        return (
                            <label
                                key={scope}
                                className={`flex items-center gap-3 px-3 py-2.5 rounded-lg border cursor-pointer transition-colors ${
                                    checked
                                        ? 'border-blue-500 bg-blue-50'
                                        : 'border-slate-200 bg-white hover:bg-slate-50'
                                }`}
                            >
                                <input
                                    type="checkbox"
                                    checked={checked}
                                    onChange={() => toggleScope(scope)}
                                    className="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500"
                                />
                                <span className="min-w-0">
                                    <span className="block text-sm font-medium text-slate-800">{SCOPE_LABELS[scope]}</span>
                                    <span className="block text-xs font-mono text-slate-500">{scope}</span>
                                </span>
                            </label>
                        );
                    })}
                </div>
                <p className="mt-2 text-xs text-slate-500">
                    Scope tokens to only what the integration needs.
                </p>
            </div>

            <div>
                <label htmlFor="token-expiry" className="block text-sm font-medium text-slate-700 mb-1.5">
                    Expiration
                </label>
                <select
                    id="token-expiry"
                    value={expiration}
                    onChange={e => setExpiration(Number(e.target.value))}
                    className="w-full sm:w-64 px-3 py-2 text-sm rounded-lg border border-slate-300 bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
                >
                    {EXPIRATION_OPTIONS.map(opt => (
                        <option key={opt.value} value={opt.value}>{opt.label}</option>
                    ))}
                </select>
            </div>

            <div className="pt-1">
                <Button type="submit" variant="primary" loading={creating} disabled={!canSubmit} icon={<KeyRound size={16} />}>
                    Generate token
                </Button>
            </div>
        </form>
    );
};

// ─────────────────────────────────────────────────────────────────────────────
// New token reveal box (shown once)
// ─────────────────────────────────────────────────────────────────────────────

const NewTokenReveal: React.FC<{ token: string; onDismiss: () => void }> = ({ token, onDismiss }) => (
    <div className="bg-amber-50 border border-amber-300 rounded-xl p-5 shadow-sm">
        <div className="flex items-start gap-3">
            <AlertTriangle size={20} className="text-amber-600 shrink-0 mt-0.5" />
            <div className="min-w-0 flex-1">
                <h3 className="text-sm font-semibold text-amber-900">Copy your new token now</h3>
                <p className="mt-1 text-sm text-amber-800">
                    This is the only time the full token is shown. Store it somewhere safe — you will not be able to see it again.
                </p>
                <div className="mt-3 flex items-center gap-2">
                    <code className="flex-1 min-w-0 px-3 py-2 text-sm font-mono bg-white border border-amber-300 rounded-lg text-slate-800 break-all">
                        {token}
                    </code>
                    <CopyButton value={token} label="token" />
                </div>
                <div className="mt-3">
                    <button
                        type="button"
                        onClick={onDismiss}
                        className="text-sm font-medium text-amber-800 hover:text-amber-900 underline"
                    >
                        I've saved it — dismiss
                    </button>
                </div>
            </div>
        </div>
    </div>
);

// ─────────────────────────────────────────────────────────────────────────────
// Page
// ─────────────────────────────────────────────────────────────────────────────

export const ApiTokensPage: React.FC = () => {
    const successToast = useSuccessToast();
    const errorToast = useErrorToast();

    const [tokens, setTokens] = useState<ApiToken[]>([]);
    const [loading, setLoading] = useState(true);
    const [loadError, setLoadError] = useState<string | null>(null);
    const [creating, setCreating] = useState(false);
    const [newToken, setNewToken] = useState<string | null>(null);
    const [revokeTarget, setRevokeTarget] = useState<ApiToken | null>(null);
    const [revoking, setRevoking] = useState(false);

    const loadTokens = useCallback(async () => {
        setLoading(true);
        setLoadError(null);
        try {
            const result = await apiTokensAPI.list();
            setTokens(result);
        } catch (error) {
            setLoadError(getErrorMessage(error, 'Failed to load API tokens.'));
        } finally {
            setLoading(false);
        }
    }, []);

    useEffect(() => {
        void loadTokens();
    }, [loadTokens]);

    const handleCreate = useCallback(async (name: string, scopes: string, expiresInDays: number) => {
        setCreating(true);
        try {
            const body = expiresInDays > 0
                ? { name, scopes, expires_in_days: expiresInDays }
                : { name, scopes };
            const response = await apiTokensAPI.create(body);
            setNewToken(response.token);
            successToast('Token created');
            await loadTokens();
        } catch (error) {
            errorToast(getErrorMessage(error, 'Failed to create token.'));
        } finally {
            setCreating(false);
        }
    }, [loadTokens, successToast, errorToast]);

    const handleRevoke = useCallback(async () => {
        if (!revokeTarget) return;
        setRevoking(true);
        try {
            await apiTokensAPI.revoke(revokeTarget.id);
            successToast('Token revoked');
            setRevokeTarget(null);
            await loadTokens();
        } catch (error) {
            errorToast(getErrorMessage(error, 'Failed to revoke token.'));
        } finally {
            setRevoking(false);
        }
    }, [revokeTarget, loadTokens, successToast, errorToast]);

    const activeTokens = useMemo(() => tokens.filter(t => !t.is_revoked), [tokens]);
    const revokedTokens = useMemo(() => tokens.filter(t => t.is_revoked), [tokens]);
    const orderedTokens = useMemo(() => [...activeTokens, ...revokedTokens], [activeTokens, revokedTokens]);

    return (
        <div className="h-full overflow-y-auto bg-slate-50">
            <div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
                {/* Header */}
                <div className="mb-6">
                    <div className="flex items-center gap-3">
                        <div className="w-11 h-11 rounded-xl bg-blue-50 flex items-center justify-center">
                            <KeyRound size={22} className="text-blue-600" />
                        </div>
                        <div>
                            <h1 className="text-2xl font-bold text-slate-900 tracking-tight">API tokens</h1>
                            <p className="text-sm text-slate-500 mt-0.5">
                                Personal access tokens let scripts, CLIs, and MCP clients act with your permissions.
                            </p>
                        </div>
                    </div>
                </div>

                {newToken && (
                    <div className="mb-6">
                        <NewTokenReveal token={newToken} onDismiss={() => setNewToken(null)} />
                    </div>
                )}

                <div className="mb-6">
                    <CreateTokenForm creating={creating} onCreate={handleCreate} />
                </div>

                {/* Existing tokens */}
                <div className="bg-white border border-slate-200 rounded-xl shadow-sm overflow-hidden">
                    <div className="px-6 py-4 border-b border-slate-100 flex items-center justify-between">
                        <h2 className="text-base font-semibold text-slate-900">Your tokens</h2>
                        {!loading && (
                            <span className="text-xs text-slate-500">
                                {activeTokens.length} active{revokedTokens.length ? `, ${revokedTokens.length} revoked` : ''}
                            </span>
                        )}
                    </div>

                    {loading ? (
                        <div className="flex items-center justify-center gap-2 py-16 text-slate-500">
                            <Loader2 size={18} className="animate-spin" />
                            <span className="text-sm">Loading tokens…</span>
                        </div>
                    ) : loadError ? (
                        <div className="px-6 py-12 text-center">
                            <AlertTriangle size={22} className="text-red-500 mx-auto mb-2" />
                            <p className="text-sm text-slate-700">{loadError}</p>
                            <div className="mt-4">
                                <Button variant="secondary" size="sm" onClick={() => void loadTokens()}>Try again</Button>
                            </div>
                        </div>
                    ) : orderedTokens.length === 0 ? (
                        <div className="px-6 py-16 text-center">
                            <ShieldCheck size={26} className="text-slate-300 mx-auto mb-3" />
                            <p className="text-sm font-medium text-slate-700">No tokens yet</p>
                            <p className="text-sm text-slate-500 mt-1">Create one above to connect a script or MCP client.</p>
                        </div>
                    ) : (
                        <div className="overflow-x-auto">
                            <table className="w-full text-sm">
                                <thead>
                                    <tr className="text-left text-xs font-semibold text-slate-500 uppercase tracking-wide bg-slate-50">
                                        <th className="px-6 py-3">Name</th>
                                        <th className="px-6 py-3">Prefix</th>
                                        <th className="px-6 py-3">Scopes</th>
                                        <th className="px-6 py-3">Last used</th>
                                        <th className="px-6 py-3">Expires</th>
                                        <th className="px-6 py-3 text-right">Actions</th>
                                    </tr>
                                </thead>
                                <tbody className="divide-y divide-slate-100">
                                    {orderedTokens.map(token => (
                                        <tr key={token.id} className={token.is_revoked ? 'opacity-60' : ''}>
                                            <td className="px-6 py-4">
                                                <div className="flex items-center gap-2">
                                                    <span className="font-medium text-slate-900">{token.name}</span>
                                                    {token.is_revoked && <Badge variant="danger">Revoked</Badge>}
                                                </div>
                                                <span className="text-xs text-slate-400">Created {formatDate(token.created_date)}</span>
                                            </td>
                                            <td className="px-6 py-4">
                                                <code className="text-xs font-mono text-slate-600">{token.token_prefix}…</code>
                                            </td>
                                            <td className="px-6 py-4">
                                                <div className="flex flex-wrap gap-1">
                                                    {scopeList(token.scopes).length === 0 ? (
                                                        // An omitted/empty scope means the token is unrestricted
                                                        // (full access) per the API contract; show that
                                                        // explicitly so it is never mistaken for "no access".
                                                        <span
                                                            title="This token has no scope restriction and can do everything the owner can."
                                                            className="inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium bg-amber-100 text-amber-700"
                                                        >
                                                            Full access
                                                        </span>
                                                    ) : (
                                                        scopeList(token.scopes).map(scope => (
                                                            <span
                                                                key={scope}
                                                                className="inline-flex items-center px-2 py-0.5 rounded-md text-xs font-mono bg-slate-100 text-slate-600"
                                                            >
                                                                {scope}
                                                            </span>
                                                        ))
                                                    )}
                                                </div>
                                            </td>
                                            <td className="px-6 py-4 text-slate-600">{formatDate(token.last_used_date)}</td>
                                            <td className="px-6 py-4 text-slate-600">
                                                {token.expires_at ? formatDate(token.expires_at) : 'Never'}
                                            </td>
                                            <td className="px-6 py-4 text-right">
                                                {!token.is_revoked && (
                                                    <button
                                                        type="button"
                                                        onClick={() => setRevokeTarget(token)}
                                                        className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-lg text-red-600 hover:bg-red-50 transition-colors"
                                                    >
                                                        <Trash2 size={15} />
                                                        <span>Revoke</span>
                                                    </button>
                                                )}
                                            </td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        </div>
                    )}
                </div>
            </div>

            <ConfirmationModal
                isOpen={!!revokeTarget}
                title="Revoke token"
                message={`Revoke "${revokeTarget?.name ?? ''}"? Any script or client using this token will immediately lose access. This cannot be undone.`}
                confirmLabel="Revoke"
                variant="danger"
                loading={revoking}
                onConfirm={() => void handleRevoke()}
                onClose={() => (revoking ? undefined : setRevokeTarget(null))}
            />
        </div>
    );
};

export default ApiTokensPage;
