import React, { useState, useEffect, useRef } from 'react';
import { X, Loader, AlertCircle, CheckCircle, RefreshCw, ChevronDown, ChevronUp } from 'lucide-react';

const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';

const FETCH_DELAY_MS = 7000; // 7 seconds between requests (safe for 5 req/30s without API key)
const RETRY_DELAY_MS = 35000; // Wait 35 seconds on 429 before retry

export default function NvdSyncModal({ onClose, onSyncComplete }) {
  const [phase, setPhase] = useState('idle'); // idle, fetching, review, applying, done
  const [cveIds, setCveIds] = useState([]);
  const [fetchProgress, setFetchProgress] = useState({ current: 0, total: 0, currentId: '' });
  const [results, setResults] = useState({}); // { cveId: { nvd: {...}, current: {...}, status: 'found'|'not_found'|'error'|'no_change', error: '' } }
  const [descriptionChoices, setDescriptionChoices] = useState({}); // { cveId: 'keep' | 'nvd' }
  const [applyResult, setApplyResult] = useState(null);
  const [expandedDesc, setExpandedDesc] = useState({});
  const abortRef = useRef(null);

  // Fetch distinct CVE IDs on mount
  useEffect(() => {
    (async () => {
      try {
        const response = await fetch(`${API_BASE}/cves/distinct-ids`, { credentials: 'include' });
        if (!response.ok) throw new Error('Failed to fetch CVE list');
        const data = await response.json();
        setCveIds(data);
      } catch (err) {
        console.error('Error fetching CVE IDs:', err);
      }
    })();
  }, []);

  const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

  const fetchNvdData = async () => {
    setPhase('fetching');
    const controller = new AbortController();
    abortRef.current = controller;
    const newResults = {};
    setFetchProgress({ current: 0, total: cveIds.length, currentId: '' });

    // First fetch current data for all CVEs
    let currentData = {};
    try {
      const response = await fetch(`${API_BASE}/cves`, { credentials: 'include', signal: controller.signal });
      if (response.ok) {
        const allCves = await response.json();
        // Group by cve_id, take first entry for description/severity/date
        allCves.forEach(cve => {
          if (!currentData[cve.cve_id]) {
            currentData[cve.cve_id] = {
              description: cve.description,
              severity: cve.severity,
              published_date: cve.published_date
            };
          }
        });
      }
    } catch (err) {
      if (err.name === 'AbortError') { setPhase('idle'); return; }
    }

    for (let i = 0; i < cveIds.length; i++) {
      if (controller.signal.aborted) break;

      const cveId = cveIds[i];
      setFetchProgress({ current: i + 1, total: cveIds.length, currentId: cveId });

      try {
        let response = await fetch(`${API_BASE}/nvd/lookup/${encodeURIComponent(cveId)}`, {
          credentials: 'include',
          signal: controller.signal
        });

        // Handle rate limit with one retry
        if (response.status === 429) {
          await sleep(RETRY_DELAY_MS);
          if (controller.signal.aborted) break;
          response = await fetch(`${API_BASE}/nvd/lookup/${encodeURIComponent(cveId)}`, {
            credentials: 'include',
            signal: controller.signal
          });
        }

        if (response.status === 404) {
          newResults[cveId] = { status: 'not_found', current: currentData[cveId] || {} };
        } else if (!response.ok) {
          const data = await response.json().catch(() => ({}));
          newResults[cveId] = { status: 'error', error: data.error || `HTTP ${response.status}`, current: currentData[cveId] || {} };
        } else {
          const nvd = await response.json();
          const current = currentData[cveId] || {};

          const descChanged = nvd.description && nvd.description !== current.description;
          const sevChanged = nvd.severity && nvd.severity !== current.severity;
          const dateChanged = nvd.published_date && nvd.published_date !== current.published_date;

          if (!descChanged && !sevChanged && !dateChanged) {
            newResults[cveId] = { status: 'no_change', nvd, current };
          } else {
            newResults[cveId] = { status: 'found', nvd, current, descChanged, sevChanged, dateChanged };
          }
        }
      } catch (err) {
        if (err.name === 'AbortError') break;
        newResults[cveId] = { status: 'error', error: err.message, current: currentData[cveId] || {} };
      }

      // Update results progressively
      setResults({ ...newResults });

      // Rate limit delay (skip after last item)
      if (i < cveIds.length - 1 && !controller.signal.aborted) {
        await sleep(FETCH_DELAY_MS);
      }
    }

    if (!controller.signal.aborted) {
      setResults({ ...newResults });
      // Default all description choices to 'keep'
      const choices = {};
      Object.entries(newResults).forEach(([id, r]) => {
        if (r.status === 'found' && r.descChanged) {
          choices[id] = 'keep';
        }
      });
      setDescriptionChoices(choices);
      setPhase('review');
    }
  };

  const cancelFetch = () => {
    if (abortRef.current) abortRef.current.abort();
    setPhase('idle');
  };

  const setBulkDescriptionChoice = (choice) => {
    const newChoices = {};
    Object.keys(descriptionChoices).forEach(id => {
      newChoices[id] = choice;
    });
    setDescriptionChoices(newChoices);
  };

  const getChangesCount = () => {
    let count = 0;
    Object.entries(results).forEach(([id, r]) => {
      if (r.status === 'found') {
        if (r.sevChanged || r.dateChanged || (r.descChanged && descriptionChoices[id] === 'nvd')) {
          count++;
        }
      }
    });
    return count;
  };

  const applyChanges = async () => {
    setPhase('applying');
    const updates = [];

    Object.entries(results).forEach(([cveId, r]) => {
      if (r.status !== 'found') return;

      const update = { cve_id: cveId };
      let hasChange = false;

      if (r.sevChanged) {
        update.severity = r.nvd.severity;
        hasChange = true;
      }
      if (r.dateChanged) {
        update.published_date = r.nvd.published_date;
        hasChange = true;
      }
      if (r.descChanged && descriptionChoices[cveId] === 'nvd') {
        update.description = r.nvd.description;
        hasChange = true;
      }

      if (hasChange) updates.push(update);
    });

    if (updates.length === 0) {
      setApplyResult({ updated: 0, message: 'No changes to apply' });
      setPhase('done');
      return;
    }

    try {
      const response = await fetch(`${API_BASE}/cves/nvd-sync`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ updates })
      });

      if (!response.ok) {
        const data = await response.json().catch(() => ({}));
        throw new Error(data.error || 'Sync failed');
      }

      const data = await response.json();
      setApplyResult(data);
      onSyncComplete();
    } catch (err) {
      setApplyResult({ error: err.message });
    }
    setPhase('done');
  };

  const truncate = (str, len = 120) => str && str.length > len ? str.substring(0, len) + '...' : str;

  // Summary counts
  const foundCount = Object.values(results).filter(r => r.status === 'found').length;
  const noChangeCount = Object.values(results).filter(r => r.status === 'no_change').length;
  const notFoundCount = Object.values(results).filter(r => r.status === 'not_found').length;
  const errorCount = Object.values(results).filter(r => r.status === 'error').length;

  return (
    <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
      <div className="bg-white rounded-lg shadow-xl max-w-5xl w-full max-h-[90vh] flex flex-col">
        {/* Header */}
        <div className="p-6 border-b border-gray-200 flex justify-between items-center flex-shrink-0">
          <div>
            <h2 className="text-2xl font-bold text-gray-900 flex items-center gap-2">
              <RefreshCw className="w-6 h-6 text-green-600" />
              Sync with NVD
            </h2>
            <p className="text-sm text-gray-500 mt-1">Update existing CVE entries with data from the National Vulnerability Database</p>
          </div>
          <button onClick={onClose} className="text-gray-400 hover:text-gray-600">
            <X className="w-6 h-6" />
          </button>
        </div>

        {/* Body */}
        <div className="p-6 overflow-y-auto flex-1">
          {/* Idle Phase */}
          {phase === 'idle' && (
            <div className="text-center py-8">
              <p className="text-lg text-gray-700 mb-2">
                {cveIds.length > 0
                  ? <><strong>{cveIds.length}</strong> unique CVE{cveIds.length !== 1 ? 's' : ''} in database</>
                  : 'Loading CVE count...'}
              </p>
              <p className="text-sm text-gray-500 mb-6">
                This will fetch data from NVD for each CVE and let you review changes before applying.
                Rate-limited to stay within NVD API limits.
              </p>
              <button
                onClick={fetchNvdData}
                disabled={cveIds.length === 0}
                className="px-6 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors font-medium shadow-md disabled:opacity-50 flex items-center gap-2 mx-auto"
              >
                <RefreshCw className="w-5 h-5" />
                Fetch NVD Data
              </button>
            </div>
          )}

          {/* Fetching Phase */}
          {phase === 'fetching' && (
            <div className="py-8">
              <div className="text-center mb-6">
                <Loader className="w-8 h-8 text-green-600 animate-spin mx-auto mb-3" />
                <p className="text-lg text-gray-700">
                  Fetching CVE {fetchProgress.current} of {fetchProgress.total}
                </p>
                <p className="text-sm text-gray-500 font-mono mt-1">{fetchProgress.currentId}</p>
              </div>
              {/* Progress bar */}
              <div className="w-full bg-gray-200 rounded-full h-3 mb-4">
                <div
                  className="bg-green-600 h-3 rounded-full transition-all duration-300"
                  style={{ width: `${fetchProgress.total > 0 ? (fetchProgress.current / fetchProgress.total) * 100 : 0}%` }}
                />
              </div>
              <div className="text-center">
                <button
                  onClick={cancelFetch}
                  className="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"
                >
                  Cancel
                </button>
              </div>
            </div>
          )}

          {/* Review Phase */}
          {phase === 'review' && (
            <div>
              {/* Summary bar */}
              <div className="flex flex-wrap gap-3 mb-4 p-3 bg-gray-50 rounded-lg text-sm">
                <span className="font-medium">Found: <span className="text-green-700">{foundCount}</span></span>
                <span>|</span>
                <span>Up to date: <span className="text-gray-600">{noChangeCount}</span></span>
                <span>|</span>
                <span>Changes: <span className="text-blue-700">{foundCount}</span></span>
                <span>|</span>
                <span>Not in NVD: <span className="text-gray-400">{notFoundCount}</span></span>
                <span>|</span>
                <span>Errors: <span className="text-red-600">{errorCount}</span></span>
              </div>

              {/* Bulk controls */}
              {Object.keys(descriptionChoices).length > 0 && (
                <div className="flex gap-2 mb-4">
                  <span className="text-sm text-gray-600 self-center">Descriptions:</span>
                  <button
                    onClick={() => setBulkDescriptionChoice('keep')}
                    className="px-3 py-1 text-xs rounded border border-gray-300 hover:bg-gray-100 transition-colors"
                  >
                    Keep All Existing
                  </button>
                  <button
                    onClick={() => setBulkDescriptionChoice('nvd')}
                    className="px-3 py-1 text-xs rounded border border-green-300 text-green-700 hover:bg-green-50 transition-colors"
                  >
                    Use All NVD
                  </button>
                </div>
              )}

              {/* Comparison table */}
              <div className="space-y-2">
                {Object.entries(results).map(([cveId, r]) => {
                  if (r.status === 'no_change') {
                    return (
                      <div key={cveId} className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg text-sm">
                        <span className="text-gray-400">—</span>
                        <span className="font-mono font-medium text-gray-500">{cveId}</span>
                        <span className="text-gray-400">No changes needed</span>
                      </div>
                    );
                  }

                  if (r.status === 'not_found') {
                    return (
                      <div key={cveId} className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg text-sm">
                        <span className="text-gray-400">—</span>
                        <span className="font-mono font-medium text-gray-400">{cveId}</span>
                        <span className="text-gray-400 italic">Not found in NVD</span>
                      </div>
                    );
                  }

                  if (r.status === 'error') {
                    return (
                      <div key={cveId} className="flex items-center gap-3 p-3 bg-red-50 rounded-lg text-sm">
                        <AlertCircle className="w-4 h-4 text-red-500 flex-shrink-0" />
                        <span className="font-mono font-medium text-gray-700">{cveId}</span>
                        <span className="text-red-600">{r.error}</span>
                      </div>
                    );
                  }

                  // status === 'found' — show changes
                  const isExpanded = expandedDesc[cveId];
                  return (
                    <div key={cveId} className="border border-gray-200 rounded-lg p-4 bg-white">
                      <div className="flex items-start gap-3">
                        <CheckCircle className="w-4 h-4 text-green-500 mt-1 flex-shrink-0" />
                        <div className="flex-1 min-w-0">
                          <div className="flex items-center gap-3 mb-2 flex-wrap">
                            <span className="font-mono font-bold text-gray-900">{cveId}</span>
                            {r.sevChanged && (
                              <span className="text-xs">
                                Severity: <span className="text-red-600">{r.current.severity}</span>
                                {' → '}
                                <span className="text-green-700">{r.nvd.severity}</span>
                              </span>
                            )}
                            {r.dateChanged && (
                              <span className="text-xs">
                                Date: <span className="text-red-600">{r.current.published_date || '(none)'}</span>
                                {' → '}
                                <span className="text-green-700">{r.nvd.published_date}</span>
                              </span>
                            )}
                          </div>

                          {r.descChanged && (
                            <div className="mt-2">
                              <div className="flex items-center gap-2 mb-2">
                                <span className="text-xs font-medium text-gray-600">Description:</span>
                                <button
                                  onClick={() => setExpandedDesc(prev => ({ ...prev, [cveId]: !prev[cveId] }))}
                                  className="text-xs text-blue-600 hover:text-blue-800 flex items-center gap-1"
                                >
                                  {isExpanded ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
                                  {isExpanded ? 'Collapse' : 'Expand'}
                                </button>
                              </div>
                              {isExpanded ? (
                                <div className="space-y-2 text-xs">
                                  <div className="p-2 bg-red-50 rounded border border-red-200">
                                    <span className="font-medium text-red-700">Current: </span>
                                    <span className="text-gray-700">{r.current.description || '(empty)'}</span>
                                  </div>
                                  <div className="p-2 bg-green-50 rounded border border-green-200">
                                    <span className="font-medium text-green-700">NVD: </span>
                                    <span className="text-gray-700">{r.nvd.description}</span>
                                  </div>
                                </div>
                              ) : (
                                <p className="text-xs text-gray-500">{truncate(r.nvd.description)}</p>
                              )}

                              {/* Description choice */}
                              <div className="flex gap-4 mt-2">
                                <label className="flex items-center gap-1 text-xs cursor-pointer">
                                  <input
                                    type="radio"
                                    name={`desc-${cveId}`}
                                    checked={descriptionChoices[cveId] === 'keep'}
                                    onChange={() => setDescriptionChoices(prev => ({ ...prev, [cveId]: 'keep' }))}
                                    className="text-blue-600"
                                  />
                                  Keep existing
                                </label>
                                <label className="flex items-center gap-1 text-xs cursor-pointer">
                                  <input
                                    type="radio"
                                    name={`desc-${cveId}`}
                                    checked={descriptionChoices[cveId] === 'nvd'}
                                    onChange={() => setDescriptionChoices(prev => ({ ...prev, [cveId]: 'nvd' }))}
                                    className="text-green-600"
                                  />
                                  Use NVD
                                </label>
                              </div>
                            </div>
                          )}
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* Applying Phase */}
          {phase === 'applying' && (
            <div className="text-center py-12">
              <Loader className="w-10 h-10 text-green-600 animate-spin mx-auto mb-4" />
              <p className="text-lg text-gray-700">Applying changes...</p>
            </div>
          )}

          {/* Done Phase */}
          {phase === 'done' && applyResult && (
            <div className="text-center py-8">
              {applyResult.error ? (
                <>
                  <AlertCircle className="w-12 h-12 text-red-500 mx-auto mb-4" />
                  <p className="text-lg text-red-700 font-medium mb-2">Sync failed</p>
                  <p className="text-sm text-gray-600">{applyResult.error}</p>
                </>
              ) : (
                <>
                  <CheckCircle className="w-12 h-12 text-green-500 mx-auto mb-4" />
                  <p className="text-lg text-green-700 font-medium mb-2">Sync complete</p>
                  <p className="text-sm text-gray-600">
                    {applyResult.updated} row{applyResult.updated !== 1 ? 's' : ''} updated
                  </p>
                  {applyResult.errors && applyResult.errors.length > 0 && (
                    <p className="text-sm text-amber-600 mt-2">
                      {applyResult.errors.length} error{applyResult.errors.length !== 1 ? 's' : ''} occurred
                    </p>
                  )}
                </>
              )}
            </div>
          )}
        </div>

        {/* Footer */}
        <div className="p-6 border-t border-gray-200 flex justify-end gap-3 flex-shrink-0">
          {phase === 'review' && (
            <>
              <button
                onClick={onClose}
                className="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"
              >
                Cancel
              </button>
              <button
                onClick={applyChanges}
                disabled={getChangesCount() === 0}
                className="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors font-medium shadow-md disabled:opacity-50"
              >
                Apply {getChangesCount()} Change{getChangesCount() !== 1 ? 's' : ''}
              </button>
            </>
          )}
          {phase === 'done' && (
            <button
              onClick={onClose}
              className="px-6 py-2 bg-[#0476D9] text-white rounded-lg hover:bg-[#0360B8] transition-colors font-medium shadow-md"
            >
              Close
            </button>
          )}
        </div>
      </div>
    </div>
  );
}
