import { useState, useEffect, useRef, useMemo } from 'react';
import { api, csrfQuery } from '../api.js';
import CompareTable from '../components/CompareTable.jsx';
import DiffPanel from '../components/DiffPanel.jsx';
import EmptyState from '../components/EmptyState.jsx';
import OrgBar from '../components/OrgBar.jsx';
import StatCard from '../components/StatCard.jsx';
import FilterTabs from '../components/FilterTabs.jsx';
import { IconArrowRight, IconX, IconCopy, IconCheck } from '../Icons.jsx';

const LOCAL_OPT = { id: 'local', label: 'Local Source', alias: 'local' };

function formatRelativeTime(dateStr) {
  if (!dateStr) return null;
  const d = new Date(dateStr);
  if (isNaN(d)) return null;
  const diffMs = Date.now() - d.getTime();
  const diffSec = Math.round(diffMs / 1000);
  if (diffSec < 60) return 'just now';
  const diffMin = Math.round(diffSec / 60);
  if (diffMin < 60) return `${diffMin} min ago`;
  const diffHr = Math.round(diffMin / 60);
  const diffDay = Math.floor(diffHr / 24);
  if (diffDay < 1) return `${diffHr}h ago`;
  return `${diffDay}d ago`;
}

export default function ComparePage() {
  const [orgs, setOrgs]                 = useState([]);
  const [source, setSource]             = useState(LOCAL_OPT);
  const [target, setTarget]             = useState(null);
  const [running, setRunning]           = useState(false);
  const [items, setItems]               = useState([]);
  const [hasResult, setHasResult]       = useState(false);
  const [phase2Active, setPhase2Active] = useState(false);
  const [phase2Done, setPhase2Done]     = useState(0);
  const [phase2Total, setPhase2Total]   = useState(0);
  const [streamError, setStreamError]   = useState(null);
  const [diffItem, setDiffItem]         = useState(null);
  const [selectedItems, setSelectedItems] = useState([]);
  const [manifestXml, setManifestXml]     = useState('');
  const [manifestOpen, setManifestOpen]   = useState(false);
  const [suggestedVersion, setSuggestedVersion] = useState('');
  const [version, setVersion]             = useState('');
  const [saving, setSaving]               = useState(false);
  const [saveSuccess, setSaveResult]      = useState(null);
  const [lastScannedDate, setLastScannedDate] = useState(null);
  const [activeTab, setActiveTab]         = useState('All');
  const esRef = useRef(null);
  const mountedRef = useRef(true);

  useEffect(() => {
    api.orgs()
      .then(({ orgs: list }) => setOrgs(list ?? []))
      .catch(() => {});
  }, []);

  useEffect(() => {
    mountedRef.current = true;
    return () => {
      mountedRef.current = false;
      esRef.current?.close();
    };
  }, []);

  // Bug 3: load cached compare results on mount
  useEffect(() => {
    api.compareResult()
      .then((data) => {
        if (data?.items?.length) {
          setItems(data.items);
          setHasResult(true);
          if (data.target) setTarget({ id: data.target, label: data.target });
          if (data.date) setLastScannedDate(data.date);
        }
      })
      .catch(() => {});
  }, []);

  const allOrgs = useMemo(() => [LOCAL_OPT, ...orgs.map((o) => ({ id: o.alias, label: o.alias, alias: o.alias }))], [orgs]);

  const startPhase2 = async () => {
    esRef.current?.close();
    setPhase2Active(true);
    setPhase2Done(0);

    // CSRF: the stream endpoint accepts the token via ?csrf because
    // EventSource can't set custom headers.
    const query = await csrfQuery();
    const es = new EventSource(`/api/compare/stream?${query}`);
    esRef.current = es;

    es.onmessage = (e) => {
      if (!mountedRef.current) return;
      let event;
      try { event = JSON.parse(e.data); } catch { return; }
      if (event.type === 'progress') {
        setPhase2Total(event.total);
        setPhase2Done(event.completed);
      } else if (event.type === 'diff') {
        setItems((prev) => prev.map((i) =>
          i.type === event.itemType && i.member === event.member
            ? { ...i, status: event.status }
            : i,
        ));
      } else if (event.type === 'done') {
        setPhase2Active(false);
        es.close();
      } else if (event.type === 'error') {
        setStreamError(event.message ?? 'Content comparison failed.');
        setPhase2Active(false);
        es.close();
      }
    };
    es.onerror = () => {
      if (!mountedRef.current) return;
      setStreamError('Streaming failed. The inventory was saved — click Run again to retry.');
      setPhase2Active(false);
      setPhase2Done(0);
      setPhase2Total(0);
      es.close();
    };
  };

  const handleRun = async () => {
    if (!target) return;
    setStreamError(null);
    setRunning(true);
    setItems([]);
    setHasResult(false);
    setPhase2Active(false);
    setActiveTab('All');
    try {
      const result = await api.runCompare(source.id, target.id);
      setItems(result.items ?? []);
      setHasResult(true);
      setLastScannedDate(result.date ?? new Date().toISOString());
      if ((result.items ?? []).some((i) => i.status === 'both')) startPhase2();
    } catch (err) {
      console.error('Compare failed', err);
    } finally {
      setRunning(false);
    }
  };

  const handleBuildManifest = async (selected) => {
    setSelectedItems(selected);
    setSaveResult(null);
    try {
      const { xml } = await api.buildManifest(selected.map(({ type, member }) => ({ type, member })));
      setManifestXml(xml);
      setManifestOpen(true);

      // Fetch suggested version
      api.suggestVersion().then(d => {
        setSuggestedVersion(d.version);
        setVersion(d.version);
      }).catch(() => {});
    } catch (err) {
      console.error('Manifest build failed', err);
    }
  };

  const handleRemoveFromManifest = async (type, member) => {
    const next = selectedItems.filter(i => !(i.type === type && i.member === member));
    setSelectedItems(next);
    if (next.length === 0) {
      setManifestXml('');
      return;
    }
    const { xml } = await api.buildManifest(next.map(i => ({ type: i.type, member: i.member })));
    setManifestXml(xml);
  };

  const handleSaveManifest = async () => {
    setSaving(true);
    setSaveResult(null);
    try {
      const res = await api.buildManifest(selectedItems.map(({ type, member }) => ({ type, member })), {
        save: true,
        version: version || suggestedVersion
      });
      setSaveResult(res);
    } catch (err) {
      alert(`Save failed: ${err.message}`);
    } finally {
      setSaving(false);
    }
  };

  // Derived counts for stats row and FilterTabs
  const totalCount    = items.length;
  const matchedCount  = items.filter((i) => i.status === 'identical' || i.status === 'match').length;
  const modifiedCount = items.filter((i) => i.status === 'modified' || i.status === 'both').length;
  const addedCount    = items.filter((i) => i.status === 'source-only').length;
  const removedCount  = items.filter((i) => i.status === 'target-only').length;
  const conflictCount = items.filter((i) => i.status === 'conflict').length;
  const diffCount     = totalCount - matchedCount;
  const matchRate     = totalCount > 0 ? (matchedCount / totalCount * 100).toFixed(1) + '%' : '—';

  const filterTabs = [
    { label: 'All',       count: totalCount },
    { label: 'Modified',  count: modifiedCount, variant: 'mod' },
    { label: 'Added',     count: addedCount,    variant: 'add' },
    { label: 'Removed',   count: removedCount,  variant: 'rm' },
    { label: 'Conflicts', count: conflictCount, variant: 'conflict' },
  ];

  const phase2Pct = phase2Total > 0 ? Math.round((phase2Done / phase2Total) * 100) : 0;

  return (
    <div>
      <div className="page-header">
        <div className="page-header-text">
          <h1>Compare</h1>
          <p className="page-subtitle">Compare metadata between orgs or local source</p>
        </div>
      </div>

      {/* Org selector */}
      <div className="card mb-4">
        <div className="card-head">
          <div className="card-title">Configure comparison</div>
        </div>
        <div className="card-body">
          <div style={{ display: 'flex', alignItems: 'flex-end', gap: 'var(--s-4)', flexWrap: 'wrap' }}>
            <div className="input-field" style={{ flex: 1, minWidth: 180 }}>
              <label className="input-label">Source</label>
              <select
                className="input"
                value={source.id}
                onChange={(e) => {
                  const opt = allOrgs.find((o) => o.id === e.target.value) ?? LOCAL_OPT;
                  setSource(opt);
                }}
              >
                {allOrgs.map((o) => (
                  <option key={o.id} value={o.id}>{o.label}</option>
                ))}
              </select>
            </div>

            <div style={{ paddingBottom: 2, color: 'var(--fg-subtle)', flexShrink: 0 }}>
              <IconArrowRight size={16} />
            </div>

            <div className="input-field" style={{ flex: 1, minWidth: 180 }}>
              <label className="input-label">Target org</label>
              <select
                className="input"
                value={target?.id ?? ''}
                onChange={(e) => {
                  const opt = orgs.find((o) => o.alias === e.target.value);
                  setTarget(opt ? { id: opt.alias, label: opt.alias } : null);
                }}
              >
                <option value="">Select target org…</option>
                {orgs.map((o) => (
                  <option key={o.alias} value={o.alias}>{o.alias}</option>
                ))}
              </select>
            </div>

            <button
              className="btn btn-primary"
              disabled={!source || !target || running}
              onClick={handleRun}
              style={{ flexShrink: 0 }}
            >
              {running ? 'Running…' : 'Run comparison'}
            </button>
          </div>
          <p style={{ marginTop: 'var(--s-2)', fontSize: 'var(--fs-xs)', color: 'var(--fg-subtle)' }}>
            Use <code style={{ fontFamily: 'var(--font-mono)', background: 'var(--bg-muted)', padding: '1px 5px', borderRadius: 3 }}>local</code> for local source files, or any connected org alias.
          </p>
        </div>
      </div>

      {/* Phase 2 progress */}
      {phase2Active && (
        <div className="card mb-4">
          <div className="card-body" style={{ display: 'flex', alignItems: 'center', gap: 'var(--s-3)' }}>
            <div className="live-dot">comparing content</div>
            <div style={{ flex: 1 }}>
              <div className="progress">
                <div className="progress-fill" style={{ width: `${phase2Pct}%` }} />
              </div>
            </div>
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-xs)', color: 'var(--fg-muted)', flexShrink: 0 }}>
              {phase2Done} / {phase2Total}
            </span>
            <button
              className="btn btn-ghost btn-sm"
              onClick={() => { esRef.current?.close(); setPhase2Active(false); setPhase2Done(0); setPhase2Total(0); }}
            >
              <IconX size={11} /> Cancel
            </button>
          </div>
        </div>
      )}

      {streamError && (
        <div className="alert alert-error mb-4">
          <span>{streamError}</span>
          <button className="alert-close" onClick={() => setStreamError(null)}>
            <IconX size={14} />
          </button>
        </div>
      )}

      {running && (
        <div className="spinner-center"><div className="spinner spinner-lg" /></div>
      )}

      {!running && !hasResult && !streamError && (
        <EmptyState
          title="No comparison yet"
          message="Select a source and target above, then click Run comparison."
        />
      )}

      {!running && hasResult && (
        <>
          {/* OrgBar */}
          <OrgBar
            source={source.label}
            target={target?.label ?? '—'}
            lastScanned={formatRelativeTime(lastScannedDate)}
          />

          {/* 4-up stats row */}
          {phase2Active ? (
            <div className="stats-row">
              <StatCard label="Total Components" value={totalCount || 0} accent="brand" />
              <StatCard label="Differences" value="…" accent="amber" />
              <StatCard label="Conflicts" value="…" accent="amber" />
              <StatCard label="Match Rate" value="Comparing…" accent="amber" />
            </div>
          ) : (
            <div className="stats-row">
              <StatCard
                label="Total Components"
                value={totalCount || 0}
                accent="brand"
              />
              <StatCard
                label="Differences"
                value={diffCount || 0}
                accent={diffCount > 0 ? 'amber' : 'green'}
              />
              <StatCard
                label="Conflicts"
                value={conflictCount || 0}
                accent={conflictCount > 0 ? 'red' : 'green'}
              />
              <StatCard
                label="Match Rate"
                value={matchRate}
                accent={matchRate === '—' || parseFloat(matchRate) >= 80 ? 'green' : parseFloat(matchRate) >= 50 ? 'amber' : 'red'}
                valueColor={matchRate === '—' || parseFloat(matchRate) >= 80 ? 'success' : parseFloat(matchRate) >= 50 ? 'warning' : 'danger'}
              />
            </div>
          )}

          {/* FilterTabs */}
          <FilterTabs
            tabs={filterTabs}
            active={activeTab}
            onChange={setActiveTab}
          />

          {/* Results table */}
          <div className="card">
            <div className="card-body">
              <CompareTable
                items={items}
                onSelect={setDiffItem}
                onBuildManifest={handleBuildManifest}
                statusFilter={activeTab}
              />
            </div>
          </div>
        </>
      )}

      <DiffPanel item={diffItem} onClose={() => setDiffItem(null)} />

      {manifestOpen && (
        <div className="modal-backdrop" onClick={() => setManifestOpen(false)}>
          <div className="modal" style={{ maxWidth: 900, width: '95vw' }} onClick={(e) => e.stopPropagation()}>
            <div className="modal-head">
              <span className="modal-title">Build Release Manifest</span>
              <button className="btn btn-icon" onClick={() => setManifestOpen(false)}>
                <IconX size={15} />
              </button>
            </div>
            <div className="modal-body" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20, maxHeight: '65vh' }}>

              {/* Left Column: Component List */}
              <div style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
                <div className="section-label" style={{ marginBottom: 10 }}>Components ({selectedItems.length})</div>
                <div className="table-wrap" style={{ flex: 1, overflowY: 'auto' }}>
                  <table className="data-table">
                    <tbody>
                      {selectedItems.map((i) => (
                        <tr key={`${i.type}.${i.member}`}>
                          <td>
                            <div style={{ fontSize: 12, fontWeight: 500 }}>{i.member}</div>
                            <div style={{ fontSize: 9, color: 'var(--fg-subtle)', textTransform: 'uppercase' }}>{i.type}</div>
                          </td>
                          <td style={{ width: 40, textAlign: 'right' }}>
                            <button className="btn btn-ghost btn-xs" style={{ color: 'var(--status-conflict-fg)' }} onClick={() => handleRemoveFromManifest(i.type, i.member)}>
                              <IconX size={12} />
                            </button>
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              </div>

              {/* Right Column: XML Preview & Save */}
              <div style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
                <div className="section-label" style={{ marginBottom: 10 }}>XML Preview</div>
                <pre style={{
                  flex: 1,
                  fontFamily: 'var(--font-mono)',
                  fontSize: 11,
                  whiteSpace: 'pre-wrap',
                  wordBreak: 'break-all',
                  background: 'var(--bg-subtle)',
                  border: '1px solid var(--border-subtle)',
                  borderRadius: 'var(--r-md)',
                  padding: 'var(--s-3)',
                  overflow: 'auto',
                  color: 'var(--fg-default)',
                  margin: 0,
                }}>
                  {manifestXml}
                </pre>
              </div>
            </div>

            <div className="modal-foot" style={{ justifyContent: 'space-between' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <div className="input-field" style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
                  <label style={{ fontSize: 11, fontWeight: 600, color: 'var(--fg-muted)', whiteSpace: 'nowrap' }}>RELEASE VERSION</label>
                  <input
                    className="input"
                    style={{ width: 100, fontSize: 12, padding: '4px 8px' }}
                    value={version}
                    onChange={(e) => setVersion(e.target.value)}
                    placeholder={suggestedVersion}
                  />
                </div>
                {saveSuccess && (
                  <div style={{ fontSize: 11, color: 'var(--status-identical-fg)', display: 'flex', alignItems: 'center', gap: 4 }}>
                    <IconCheck size={14} /> Saved as {saveSuccess.filename}
                  </div>
                )}
              </div>

              <div style={{ display: 'flex', gap: 8 }}>
                <button
                  className="btn btn-secondary btn-sm"
                  onClick={() => navigator.clipboard.writeText(manifestXml ?? '')}
                >
                  <IconCopy size={12} /> Copy
                </button>
                <button className="btn btn-primary btn-sm" onClick={handleSaveManifest} disabled={saving || selectedItems.length === 0}>
                  {saving ? 'Saving...' : 'Save to Manifest Dir'}
                </button>
                <button className="btn btn-ghost btn-sm" onClick={() => setManifestOpen(false)}>
                  Close
                </button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
