"use client";

import { useEffect, useState } from 'react';
import api from '../../lib/api';
import { ArrowPathIcon, CheckBadgeIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline';

type AssetSummary = {
    asset_id: number;
    hostname: string;
    operating_system: string | null;
    policy_count: number;
    avg_score: number | null;
    worst_score: number | null;
    worst_policy: string | null;
    last_synced: string | null;
};

type GlobalSummary = {
    assets_with_compliance: number;
    total_policies: number;
    avg_score_all: number | null;
    pass_total: number;
    fail_total: number;
    not_applicable_total: number;
    worst_offenders: AssetSummary[];
};

type URSRow = {
    asset_id: number;
    hostname: string | null;
    avs: number | null;
    ass: number | null;
    urs: number | null;
    severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'NONE';
    criticality: string | null;
    criticality_factor: number | null;
    policy_count: number | null;
};

/**
 * Normalises a thrown axios/fetch error into a renderable string.
 * FastAPI returns 422 errors as { detail: [{type,loc,msg,input}, ...] }
 * — rendering that array directly crashes React with #31. Flatten to text.
 */
function formatApiError(e: any, fallback: string): string {
    const detail = e?.response?.data?.detail;
    if (!detail) return e?.message || fallback;
    if (typeof detail === 'string') return detail;
    if (Array.isArray(detail)) {
        return detail
            .map((d: any) => {
                if (typeof d === 'string') return d;
                const loc = Array.isArray(d?.loc) ? d.loc.join('.') : '';
                return `${loc ? loc + ': ' : ''}${d?.msg || JSON.stringify(d)}`;
            })
            .join('; ');
    }
    try { return JSON.stringify(detail); } catch { return fallback; }
}

function ursColor(severity: string): string {
    switch (severity) {
        case 'CRITICAL': return 'bg-red-700 text-white';
        case 'HIGH':     return 'bg-red-100 text-red-700';
        case 'MEDIUM':   return 'bg-orange-100 text-orange-700';
        case 'LOW':      return 'bg-emerald-100 text-emerald-700';
        default:         return 'bg-gray-100 text-gray-500';
    }
}

type PolicyResult = {
    id: number;
    asset_id: number;
    policy_id: string;
    policy_name: string | null;
    policy_description: string | null;
    total_checks: number;
    pass_count: number;
    fail_count: number;
    not_applicable_count: number;
    score: number | null;
    end_scan: string | null;
    last_synced: string;
};

function scoreColor(score: number | null): string {
    if (score === null) return 'text-gray-400';
    if (score >= 90) return 'text-emerald-700 font-bold';
    if (score >= 70) return 'text-yellow-700 font-bold';
    if (score >= 40) return 'text-orange-700 font-bold';
    return 'text-red-700 font-bold';
}

function scoreBg(score: number | null): string {
    if (score === null) return 'bg-gray-200';
    if (score >= 90) return 'bg-emerald-500';
    if (score >= 70) return 'bg-yellow-500';
    if (score >= 40) return 'bg-orange-500';
    return 'bg-red-500';
}

export default function CompliancePage() {
    const [summary, setSummary] = useState<GlobalSummary | null>(null);
    const [assets, setAssets] = useState<AssetSummary[]>([]);
    const [loading, setLoading] = useState(true);
    const [refreshing, setRefreshing] = useState(false);
    const [selectedAsset, setSelectedAsset] = useState<number | null>(null);
    const [assetPolicies, setAssetPolicies] = useState<PolicyResult[]>([]);
    const [policyLoading, setPolicyLoading] = useState(false);
    const [err, setErr] = useState<string | null>(null);
    const [ursRows, setUrsRows] = useState<URSRow[]>([]);
    const [avsMode, setAvsMode] = useState<'hybrid' | 'avg' | 'max'>('hybrid');
    const [impactStats, setImpactStats] = useState<Array<{benchmark: string; count: number; avg_impact: number | null}>>([]);

    const fetchAll = async () => {
        setLoading(true);
        setErr(null);
        try {
            const [s, a, u, imp] = await Promise.all([
                api.get('/api/v1/compliance/summary'),
                api.get('/api/v1/compliance/assets?limit=200'),
                api.get(`/api/v1/compliance/urs?avs_mode=${avsMode}&limit=200`),
                api.get('/api/v1/compliance/impacts/stats').catch(() => ({ data: [] })),
            ]);
            setSummary(s.data);
            setAssets(Array.isArray(a.data) ? a.data : []);
            // Defensive: backend could 500 with non-array body — never let
            // .map crash the page.
            setUrsRows(Array.isArray(u.data) ? u.data : []);
            setImpactStats(Array.isArray(imp.data) ? imp.data : []);
        } catch (e: any) {
            setErr(formatApiError(e, 'Failed to load compliance data.'));
        } finally {
            setLoading(false);
        }
    };

    useEffect(() => { fetchAll(); }, [avsMode]);

    // CSV upload state
    const [uploading, setUploading] = useState(false);
    const uploadCsvs = async (filelist: FileList | null) => {
        if (!filelist || filelist.length === 0) return;
        setUploading(true);
        setErr(null);
        try {
            const fd = new FormData();
            Array.from(filelist).forEach(f => fd.append('files', f));
            const r = await api.post('/api/v1/compliance/impacts/import', fd, {
                headers: { 'Content-Type': 'multipart/form-data' },
            });
            const d = r.data || {};
            const perFile = d.per_file || {};
            const firstFile: any = Object.values(perFile)[0] || {};
            let msg = `Impacts imported: ${d.imported ?? 0} rows from ${d.files ?? 0} files. `;
            msg += `Skipped: no-id=${d.skipped_no_id ?? 0}, not-cis=${d.skipped_not_cis ?? 0}.`;
            if ((d.imported ?? 0) === 0) {
                msg += `\n\nDIAGNOSE (first file):\n`;
                msg += `Headers: ${(firstFile.detected_headers || []).join(', ')}\n`;
                msg += `ID column used: ${firstFile.id_column_used || '(none recognised — check CSV header names)'}\n`;
                msg += `Impact column used: ${firstFile.impact_column_used || '(none — defaulting to 50)'}\n`;
                if (Array.isArray(firstFile.preview) && firstFile.preview.length > 0) {
                    msg += `Sample skipped rows:\n`;
                    firstFile.preview.forEach((p: any) => {
                        msg += `  row ${p.row}: ${p.reason}` +
                               (p.value ? ` — value="${p.value}"` : '') +
                               (p.sample ? ` — sample=${JSON.stringify(p.sample)}` : '') + '\n';
                    });
                }
            }
            alert(msg);
            await fetchAll();
        } catch (e: any) {
            setErr(formatApiError(e, 'Impact CSV upload failed.'));
        } finally {
            setUploading(false);
        }
    };

    const recomputeURS = async () => {
        try {
            await api.post(`/api/v1/compliance/urs/recompute-all?avs_mode=${avsMode}`);
            await fetchAll();
        } catch (e: any) {
            setErr(formatApiError(e, 'URS recompute failed.'));
        }
    };

    const refreshAll = async () => {
        if (!confirm('Refresh SCA results for every Wazuh-linked asset? This pulls /sca/{agent_id} per agent.')) return;
        setRefreshing(true);
        setErr(null);
        try {
            const r = await api.post('/api/v1/compliance/refresh');
            alert(`Compliance refreshed: ${r.data.assets_synced} assets, ${r.data.policies_synced} policy results.${(r.data.errors || []).length ? ' Some errors — see backend logs.' : ''}`);
            await fetchAll();
        } catch (e: any) {
            setErr(formatApiError(e, 'Refresh failed.'));
        } finally {
            setRefreshing(false);
        }
    };

    const openAsset = async (assetId: number) => {
        setSelectedAsset(assetId);
        setPolicyLoading(true);
        setAssetPolicies([]);
        try {
            const r = await api.get(`/api/v1/compliance/${assetId}`);
            setAssetPolicies(r.data);
        } catch (e) {
            setAssetPolicies([]);
        } finally {
            setPolicyLoading(false);
        }
    };

    const refreshAsset = async (assetId: number) => {
        try {
            await api.post(`/api/v1/compliance/${assetId}/refresh`);
            await openAsset(assetId);
            await fetchAll();
        } catch {
            // ignore — keep modal open
        }
    };

    if (loading) return <div className="p-8 text-center font-mono text-gray-500">Loading compliance data…</div>;

    return (
        <div className="w-full p-4 sm:p-6 lg:p-8">
            <div className="flex items-center justify-between mb-6">
                <div>
                    <h1 className="text-2xl font-bold text-gray-900 font-mono flex items-center gap-2">
                        <CheckBadgeIcon className="h-7 w-7 text-emerald-600" />
                        Compliance
                    </h1>
                    <p className="text-sm text-gray-500 font-mono mt-1">
                        Wazuh SCA results — pass / fail per check, aggregated per policy and asset.
                    </p>
                </div>
                <button
                    onClick={refreshAll}
                    disabled={refreshing}
                    className="inline-flex items-center gap-2 rounded-md bg-vulncheck-blue px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-600 disabled:opacity-50 font-mono"
                >
                    <ArrowPathIcon className={`h-4 w-4 ${refreshing ? 'animate-spin' : ''}`} />
                    {refreshing ? 'Refreshing…' : 'Refresh All'}
                </button>
            </div>

            {err && (
                <div className="mb-4 rounded-md bg-red-50 border border-red-200 p-3 text-sm text-red-700 font-mono">
                    {err}
                </div>
            )}

            {/* Global summary cards */}
            {summary && (
                <div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
                    <div className="bg-white rounded-lg border border-gray-200 shadow-sm p-4">
                        <p className="text-xs uppercase tracking-wider text-gray-500 font-mono">Avg score</p>
                        <p className={`text-3xl font-mono mt-1 ${scoreColor(summary.avg_score_all)}`}>
                            {summary.avg_score_all !== null ? `${summary.avg_score_all.toFixed(1)}%` : '—'}
                        </p>
                    </div>
                    <div className="bg-white rounded-lg border border-gray-200 shadow-sm p-4">
                        <p className="text-xs uppercase tracking-wider text-gray-500 font-mono">Assets covered</p>
                        <p className="text-3xl font-mono text-gray-900 mt-1">{summary.assets_with_compliance}</p>
                        <p className="text-xs text-gray-400 font-mono">{summary.total_policies} policy runs</p>
                    </div>
                    <div className="bg-white rounded-lg border border-gray-200 shadow-sm p-4">
                        <p className="text-xs uppercase tracking-wider text-gray-500 font-mono">Pass total</p>
                        <p className="text-3xl font-mono text-emerald-700 mt-1">{summary.pass_total.toLocaleString()}</p>
                    </div>
                    <div className="bg-white rounded-lg border border-gray-200 shadow-sm p-4">
                        <p className="text-xs uppercase tracking-wider text-gray-500 font-mono">Fail total</p>
                        <p className="text-3xl font-mono text-red-700 mt-1">{summary.fail_total.toLocaleString()}</p>
                        <p className="text-xs text-gray-400 font-mono">{summary.not_applicable_total.toLocaleString()} N/A</p>
                    </div>
                </div>
            )}

            {/* Worst offenders */}
            {summary && Array.isArray(summary.worst_offenders) && summary.worst_offenders.length > 0 && (
                <div className="bg-white rounded-lg border border-gray-200 shadow-sm p-4 mb-8">
                    <h2 className="text-sm font-bold text-gray-900 font-mono mb-3 flex items-center gap-2">
                        <ExclamationTriangleIcon className="h-4 w-4 text-orange-600" />
                        Worst-Performing Assets
                    </h2>
                    <ul className="divide-y divide-gray-100">
                        {summary.worst_offenders.map(a => (
                            <li key={a.asset_id} className="py-2 flex items-center justify-between text-sm font-mono">
                                <button onClick={() => openAsset(a.asset_id)} className="text-vulncheck-blue hover:underline text-left">
                                    {a.hostname}
                                </button>
                                <span className="text-gray-500 text-xs truncate ml-2 max-w-xs" title={a.worst_policy || ''}>
                                    worst: {a.worst_policy || '—'}
                                </span>
                                <span className={`ml-3 ${scoreColor(a.avg_score)}`}>
                                    {a.avg_score !== null ? `${a.avg_score.toFixed(1)}%` : '—'}
                                </span>
                            </li>
                        ))}
                    </ul>
                </div>
            )}

            {/* URS table */}
            <div className="bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden mb-8">
                <div className="px-4 py-3 border-b border-gray-100 bg-gray-50 flex items-center justify-between">
                    <div>
                        <h2 className="text-sm font-bold text-gray-900 font-mono">
                            Unified Risk Score (URS)
                        </h2>
                        <p className="text-[11px] text-gray-500 font-mono">
                            URS = ((AVS + ASS) / 2) × criticality. 90+ CRITICAL · 70+ HIGH · 40+ MEDIUM · 1+ LOW
                        </p>
                    </div>
                    <div className="flex items-center gap-2">
                        <select
                            value={avsMode}
                            onChange={(e) => setAvsMode(e.target.value as any)}
                            className="text-xs border border-gray-300 rounded px-2 py-1 font-mono"
                            title="AVS calculation mode"
                        >
                            <option value="hybrid">AVS: hybrid (0.7×avg + 0.3×max)</option>
                            <option value="avg">AVS: avg</option>
                            <option value="max">AVS: max</option>
                        </select>
                        <button
                            onClick={recomputeURS}
                            className="text-xs px-2 py-1 border border-gray-300 rounded hover:bg-gray-50 font-mono"
                        >
                            ↻ recompute
                        </button>
                    </div>
                </div>
                <div className="overflow-x-auto">
                    <table className="min-w-full divide-y divide-gray-200 text-sm font-mono">
                        <thead className="bg-gray-50">
                            <tr>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">Hostname</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">Criticality</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">AVS</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">ASS</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">URS</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">Severity</th>
                            </tr>
                        </thead>
                        <tbody className="divide-y divide-gray-100 bg-white">
                            {ursRows.length === 0 && (
                                <tr><td colSpan={6} className="px-4 py-6 text-center text-gray-500">No URS data — recompute first.</td></tr>
                            )}
                            {ursRows.map(r => (
                                <tr key={r.asset_id} className="hover:bg-gray-50">
                                    <td className="px-4 py-2 text-vulncheck-blue font-bold">{r.hostname || `#${r.asset_id}`}</td>
                                    <td className="px-4 py-2 text-gray-700 uppercase text-xs">{r.criticality || 'normal'}<span className="text-gray-400 ml-1">×{r.criticality_factor ?? 1.0}</span></td>
                                    <td className="px-4 py-2 text-gray-700">{r.avs !== null ? r.avs.toFixed(1) : '—'}</td>
                                    <td className="px-4 py-2 text-gray-700">{r.ass !== null ? r.ass.toFixed(1) : '—'}</td>
                                    <td className="px-4 py-2 font-bold text-gray-900">{r.urs !== null ? r.urs.toFixed(1) : '—'}</td>
                                    <td className="px-4 py-2">
                                        <span className={`inline-flex items-center rounded px-2 py-0.5 text-[10px] font-bold ${ursColor(r.severity)}`}>
                                            {r.severity}
                                        </span>
                                    </td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </div>
            </div>

            {/* Impact CSV upload */}
            <div className="bg-white rounded-lg border border-gray-200 shadow-sm p-4 mb-8">
                <div className="flex items-center justify-between mb-3">
                    <div>
                        <h2 className="text-sm font-bold text-gray-900 font-mono">Compliance Impact CSVs</h2>
                        <p className="text-[11px] text-gray-500 font-mono">
                            CIS-Benchmark exports (cis_id, title, impact). Multi-upload supported.
                            Filename → benchmark label.
                        </p>
                    </div>
                    <label className="text-xs px-3 py-2 border border-gray-300 rounded hover:bg-gray-50 font-mono cursor-pointer">
                        {uploading ? 'Uploading…' : '+ Upload CSVs'}
                        <input
                            type="file"
                            multiple
                            accept=".csv,text/csv"
                            disabled={uploading}
                            onChange={(e) => uploadCsvs(e.target.files)}
                            className="hidden"
                        />
                    </label>
                </div>
                {impactStats.length === 0 ? (
                    <p className="text-sm text-gray-400 font-mono">
                        No impact data loaded yet. Without impacts, ASS falls back to plain pass%.
                    </p>
                ) : (
                    <ul className="text-xs font-mono divide-y divide-gray-100">
                        {impactStats.map(s => (
                            <li key={s.benchmark} className="flex justify-between py-1">
                                <span className="truncate" title={s.benchmark}>{s.benchmark}</span>
                                <span className="text-gray-500">
                                    {s.count} checks · avg impact {s.avg_impact ?? '—'}
                                </span>
                            </li>
                        ))}
                    </ul>
                )}
            </div>

            {/* All assets table */}
            <div className="bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden">
                <div className="px-4 py-3 border-b border-gray-100 bg-gray-50">
                    <h2 className="text-sm font-bold text-gray-900 font-mono">All Assets</h2>
                </div>
                <div className="overflow-x-auto">
                    <table className="min-w-full divide-y divide-gray-200 text-sm font-mono">
                        <thead className="bg-gray-50">
                            <tr>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">Hostname</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">OS</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">Policies</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">Avg</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">Worst</th>
                                <th className="px-4 py-2 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">Last sync</th>
                            </tr>
                        </thead>
                        <tbody className="divide-y divide-gray-100 bg-white">
                            {assets.length === 0 && (
                                <tr><td colSpan={6} className="px-4 py-6 text-center text-gray-500">
                                    No compliance data yet. Click <b>Refresh All</b> to pull SCA results from Wazuh.
                                </td></tr>
                            )}
                            {assets.map(a => (
                                <tr key={a.asset_id} className="hover:bg-gray-50 cursor-pointer" onClick={() => openAsset(a.asset_id)}>
                                    <td className="px-4 py-2 text-vulncheck-blue font-bold">{a.hostname}</td>
                                    <td className="px-4 py-2 text-gray-600 truncate max-w-xs">{a.operating_system || '—'}</td>
                                    <td className="px-4 py-2 text-gray-700">{a.policy_count}</td>
                                    <td className={`px-4 py-2 ${scoreColor(a.avg_score)}`}>
                                        {a.avg_score !== null ? `${a.avg_score.toFixed(1)}%` : '—'}
                                    </td>
                                    <td className={`px-4 py-2 ${scoreColor(a.worst_score)}`}>
                                        {a.worst_score !== null ? `${a.worst_score.toFixed(1)}%` : '—'}
                                    </td>
                                    <td className="px-4 py-2 text-gray-500 text-xs">
                                        {a.last_synced ? new Date(a.last_synced).toLocaleString('de-CH') : '—'}
                                    </td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </div>
            </div>

            {/* Asset detail modal */}
            {selectedAsset !== null && (
                <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-gray-700/70">
                    <div className="bg-white rounded-lg shadow-xl max-w-3xl w-full max-h-[85vh] overflow-hidden flex flex-col">
                        <div className="px-4 py-3 border-b border-gray-100 flex items-center justify-between">
                            <h3 className="text-base font-bold font-mono text-gray-900">
                                Compliance — asset #{selectedAsset}
                            </h3>
                            <div className="flex items-center gap-2">
                                <button
                                    onClick={() => refreshAsset(selectedAsset)}
                                    className="text-xs px-2 py-1 border border-gray-300 rounded hover:bg-gray-50 font-mono"
                                    title="Re-fetch from Wazuh"
                                >
                                    ↻ refresh
                                </button>
                                <button
                                    onClick={() => setSelectedAsset(null)}
                                    className="text-gray-400 hover:text-gray-600 text-xl leading-none"
                                >
                                    ×
                                </button>
                            </div>
                        </div>
                        <div className="overflow-y-auto p-4 space-y-2">
                            {policyLoading && <p className="text-sm font-mono text-gray-500">Loading…</p>}
                            {!policyLoading && assetPolicies.length === 0 && (
                                <p className="text-sm font-mono text-gray-500">No policies yet — try refresh.</p>
                            )}
                            {assetPolicies.map(p => (
                                <div key={p.id} className="border border-gray-200 rounded-md p-3">
                                    <div className="flex items-baseline justify-between gap-3 mb-1">
                                        <p className="text-sm font-bold text-gray-900 font-mono truncate" title={p.policy_name || p.policy_id}>
                                            {p.policy_name || p.policy_id}
                                        </p>
                                        <span className={`text-base font-mono ${scoreColor(p.score)}`}>
                                            {p.score !== null ? `${p.score.toFixed(1)}%` : '—'}
                                        </span>
                                    </div>
                                    <div className="h-1.5 w-full overflow-hidden rounded-full bg-gray-100 mb-2">
                                        <div className={`h-full ${scoreBg(p.score)}`} style={{ width: `${p.score ?? 0}%` }} />
                                    </div>
                                    <div className="flex justify-between text-xs text-gray-500 font-mono">
                                        <span title="passed checks" className="text-emerald-700">✓ {p.pass_count}</span>
                                        <span title="failed checks"  className="text-red-700">✗ {p.fail_count}</span>
                                        <span title="not applicable" className="text-gray-500">○ {p.not_applicable_count}</span>
                                        <span title="total checks"   className="text-gray-400">Σ {p.total_checks}</span>
                                        <span className="text-gray-400 truncate">
                                            scan: {p.end_scan ? new Date(p.end_scan).toLocaleDateString('de-CH') : '—'}
                                        </span>
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>
                </div>
            )}
        </div>
    );
}
