// mod_opportunites.jsx — Module CŒUR : Opportunités + pipeline 8 étapes.
// Vue kanban (1 colonne par étape) + fiche opportunité en onglets.
// Lecture : window.Store.state ; écriture : window.Store (PATCH stage, promote).
// Style aligné sur matchings.jsx / styles.css. FR.

// ─── Pipeline : 8 étapes + terminaux ────────────────────────────────────────
const OPP_STAGES = [
  { v: 'captee',         label: 'Opportunité captée', n: 1 },
  { v: 'call_prospect',  label: 'Call prospect',      n: 2 },
  { v: 'sourcing_dev',   label: 'Sourcing dev',       n: 3 },
  { v: 'call_dev',       label: 'Call dev',           n: 4 },
  { v: 'intro_faite',    label: 'Intro faite',        n: 5 },
  { v: 'process_client', label: 'Process client',     n: 6 },
  { v: 'signe',          label: 'Signé',              n: 7 },
  { v: 'en_mission',     label: 'En mission',         n: 8 },
];
const OPP_STAGE_MAP = Object.fromEntries(OPP_STAGES.map((s) => [s.v, s]));

// Statuts terminaux (sur l'opportunité, hors progression d'étape).
const OPP_STATUS = [
  { v: 'active', label: 'Active', cls: 'turq' },
  { v: 'won',    label: 'Gagnée', cls: 'turq' },
  { v: 'paused', label: 'En pause', cls: 'warm' },
  { v: 'lost',   label: 'Perdu', cls: 'hot' },
];
const OPP_STATUS_MAP = Object.fromEntries(OPP_STATUS.map((s) => [s.v, s]));

// Statut candidats (shortlist devs).
const CAND_STATUS = [
  { v: 'sourced',   label: 'Sourcé', cls: '' },
  { v: 'contacted', label: 'Contacté', cls: '' },
  { v: 'call_done', label: 'Call fait', cls: 'brown' },
  { v: 'maybe',     label: 'MAYBE', cls: 'warm' },
  { v: 'go',        label: 'GO', cls: 'turq' },
  { v: 'no_go',     label: 'NO GO', cls: 'hot' },
];
const CAND_STATUS_MAP = Object.fromEntries(CAND_STATUS.map((s) => [s.v, s]));

const PROSPECT_LABELS = {
  client_final: 'Client final',
  esn: 'ESN',
  startup: 'Startup',
  scaleup: 'Scaleup',
  grand_compte: 'Grand compte',
};

const fmtTjm = (v) => (v != null && v !== '' ? `${v} €` : '—');

// Hook : ré-render quand le Store change.
function useStoreSnapshot() {
  const [, force] = React.useReducer((x) => x + 1, 0);
  React.useEffect(() => window.Store.subscribe(force), []);
  return window.Store.state;
}

// ─────────────────────────────────────────────────────────────────
// Vue principale : kanban du pipeline
// ─────────────────────────────────────────────────────────────────
function OpportunitesView({ onToast }) {
  const state = useStoreSnapshot();
  const [openId, setOpenId] = React.useState(null);

  const opps = state.opportunities || [];
  const orgById = React.useMemo(() => {
    const m = {};
    (state.organisations || []).forEach((o) => { m[o.id] = o; });
    return m;
  }, [state.organisations]);

  const byStage = React.useMemo(() => {
    const g = {};
    OPP_STAGES.forEach((s) => { g[s.v] = []; });
    opps.forEach((o) => { (g[o.stage] || (g[o.stage] = [])).push(o); });
    return g;
  }, [opps]);

  const advance = async (opp, dir) => {
    const idx = OPP_STAGES.findIndex((s) => s.v === opp.stage);
    const next = OPP_STAGES[idx + dir];
    if (!next) return;
    try {
      await window.Store.update('opportunities', 'opportunities', opp.id, { stage: next.v });
      onToast(`« ${opp.title} » → ${next.label}`);
    } catch (e) { onToast('Échec du déplacement'); }
  };

  const setStatus = async (opp, status) => {
    try {
      await window.Store.update('opportunities', 'opportunities', opp.id, { status });
      onToast(`Statut : ${OPP_STATUS_MAP[status]?.label || status}`);
    } catch (e) { onToast('Échec mise à jour statut'); }
  };

  const openOpp = state.opportunities.find((o) => o.id === openId) || null;

  return (
    <div className="page opp-page" style={{ padding: 16, gap: 14 }}>
      <div className="opp-board-head">
        <div>
          <span className="count-pill">{opps.length}</span>
          <span className="muted tiny" style={{ marginLeft: 8 }}>
            opportunités dans le pipeline · 8 étapes
          </span>
        </div>
      </div>

      <div className="opp-kanban">
        {OPP_STAGES.map((stage) => {
          const items = byStage[stage.v] || [];
          return (
            <div key={stage.v} className="opp-col">
              <div className="opp-col-head">
                <span className="opp-col-n">{stage.n}</span>
                <span className="opp-col-label">{stage.label}</span>
                <span className="opp-col-count">{items.length}</span>
              </div>
              <div className="opp-col-body">
                {items.map((o) => (
                  <OppCard
                    key={o.id}
                    opp={o}
                    org={orgById[o.organisation_id]}
                    candidates={(state.opportunity_candidates || []).filter((c) => c.opportunity_id === o.id)}
                    onOpen={() => setOpenId(o.id)}
                    onAdvance={(dir) => advance(o, dir)}
                  />
                ))}
                {items.length === 0 && <div className="opp-col-empty">—</div>}
              </div>
            </div>
          );
        })}
      </div>

      {openOpp && (
        <OppDetail
          opp={openOpp}
          state={state}
          orgById={orgById}
          onClose={() => setOpenId(null)}
          onAdvance={advance}
          onSetStatus={setStatus}
          onToast={onToast}
        />
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────
// Carte opportunité (kanban)
// ─────────────────────────────────────────────────────────────────
function OppCard({ opp, org, candidates, onOpen, onAdvance }) {
  const idx = OPP_STAGES.findIndex((s) => s.v === opp.stage);
  const st = OPP_STATUS_MAP[opp.status];
  const goCount = candidates.filter((c) => c.status === 'go').length;
  const totalCand = candidates.length;
  return (
    <div className="opp-card" onClick={onOpen}>
      <div className="opp-card-title">{opp.title}</div>
      <div className="opp-card-org muted tiny">
        {org ? org.name : '—'} · {PROSPECT_LABELS[opp.prospect_type] || opp.prospect_type || '—'}
      </div>
      <div className="opp-card-meta">
        <span className="opp-tjm">{fmtTjm(opp.tjm)}</span>
        {st && st.v !== 'active' && <span className={`chip sm ${st.cls}`}>{st.label}</span>}
        {totalCand > 0 && (
          <span className="muted tiny">{goCount > 0 ? `${goCount} GO · ` : ''}{totalCand} dev{totalCand > 1 ? 's' : ''}</span>
        )}
      </div>
      <div className="opp-card-nav" onClick={(e) => e.stopPropagation()}>
        <button className="opp-nav-btn" disabled={idx <= 0} title="Reculer"
                onClick={() => onAdvance(-1)}>‹</button>
        <button className="opp-nav-btn" disabled={idx >= OPP_STAGES.length - 1} title="Avancer"
                onClick={() => onAdvance(1)}>›</button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────
// Fiche opportunité (modale) : frise + onglets
// ─────────────────────────────────────────────────────────────────
const OPP_TABS = [
  { v: 'mission',     label: 'Mission' },
  { v: 'candidats',   label: 'Candidats' },
  { v: 'placement',   label: 'Placement' },
  { v: 'commissions', label: 'Commissions' },
  { v: 'contrat',     label: 'Contrat' },
  { v: 'conversation',label: 'Conversation' },
];

function OppDetail({ opp, state, orgById, onClose, onAdvance, onSetStatus, onToast }) {
  const [tab, setTab] = React.useState('mission');
  const org = orgById[opp.organisation_id];
  const contact = (state.contacts || []).find((c) => c.id === opp.contact_id);
  const candidates = (state.opportunity_candidates || []).filter((c) => c.opportunity_id === opp.id);
  const placements = (state.placements || []).filter((p) => p.opportunity_id === opp.id);
  const st = OPP_STATUS_MAP[opp.status];

  React.useEffect(() => {
    const h = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', h);
    return () => document.removeEventListener('keydown', h);
  }, [onClose]);

  return (
    <div className="opp-modal-overlay" onClick={onClose}>
      <div className="opp-modal" onClick={(e) => e.stopPropagation()}>
        <div className="opp-modal-head">
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="hstack" style={{ gap: 10, alignItems: 'baseline' }}>
              <h2 className="opp-modal-title">{opp.title}</h2>
              {st && <span className={`chip sm ${st.cls}`}>{st.label}</span>}
            </div>
            <div className="muted tiny" style={{ marginTop: 2 }}>
              {org ? org.name : '—'} · {PROSPECT_LABELS[opp.prospect_type] || opp.prospect_type || '—'}
              {opp.tjm != null ? ` · ${fmtTjm(opp.tjm)}` : ''}
            </div>
          </div>
          <button className="mer-del" onClick={onClose} title="Fermer">
            <window.Icon.x size={14} />
          </button>
        </div>

        <OppPipelineStrip opp={opp} onAdvance={onAdvance} onSetStatus={onSetStatus} />

        <div className="opp-tabs">
          {OPP_TABS.map((t) => (
            <button key={t.v}
                    className={`opp-tab ${tab === t.v ? 'is-active' : ''}`}
                    onClick={() => setTab(t.v)}>
              {t.label}
              {t.v === 'candidats' && candidates.length > 0 && (
                <span className="opp-tab-badge">{candidates.length}</span>
              )}
              {t.v === 'placement' && placements.length > 0 && (
                <span className="opp-tab-badge">{placements.length}</span>
              )}
            </button>
          ))}
        </div>

        <div className="opp-tab-body">
          {tab === 'mission' && <TabMission opp={opp} org={org} contact={contact} />}
          {tab === 'candidats' && (
            <TabCandidats opp={opp} candidates={candidates} contacts={state.contacts || []}
                          placements={placements} onToast={onToast} />
          )}
          {tab === 'placement' && (
            <TabPlacement placements={placements} contacts={state.contacts || []} />
          )}
          {tab === 'commissions' && <TabPlaceholder label="Commissions"
            text="Suivi mensuel des commissions par placement — module Commissions (à câbler)." />}
          {tab === 'contrat' && <TabPlaceholder label="Contrat"
            text="Contrat d'apport d'affaires : génération, signature, PDF (à câbler)." />}
          {tab === 'conversation' && <TabPlaceholder label="Conversation"
            text="Fil de conversation (LinkedIn / mail) lié à cette opportunité (à câbler)." />}
        </div>
      </div>
    </div>
  );
}

// Frise pipeline horizontale en tête de fiche.
function OppPipelineStrip({ opp, onAdvance, onSetStatus }) {
  const idx = OPP_STAGES.findIndex((s) => s.v === opp.stage);
  const terminal = opp.status === 'paused' || opp.status === 'lost';
  return (
    <div className="opp-strip">
      <div className="opp-strip-track">
        {OPP_STAGES.map((s, i) => {
          const cls = i < idx ? 'done' : i === idx ? 'current' : 'todo';
          return (
            <div key={s.v} className={`opp-strip-step ${cls} ${terminal && i === idx ? 'muted-step' : ''}`}>
              <span className="opp-strip-dot">{i < idx ? '✓' : s.n}</span>
              <span className="opp-strip-label">{s.label}</span>
            </div>
          );
        })}
      </div>
      <div className="opp-strip-actions">
        <button className="btn ghost" disabled={idx <= 0} onClick={() => onAdvance(opp, -1)}>
          <window.Icon.chev size={12} style={{ transform: 'rotate(180deg)' }} /> Reculer
        </button>
        <button className="btn primary" disabled={idx >= OPP_STAGES.length - 1}
                onClick={() => onAdvance(opp, 1)}>
          Avancer <window.Icon.chev size={12} />
        </button>
        <div className="spacer" style={{ flex: 1 }} />
        <StatusPicker value={opp.status} onChange={(v) => onSetStatus(opp, v)} />
      </div>
    </div>
  );
}

function StatusPicker({ value, onChange }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  const opt = OPP_STATUS_MAP[value] || OPP_STATUS[0];
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button className={`chip sm ${opt.cls}`} onClick={() => setOpen((o) => !o)}
              style={{ cursor: 'pointer' }}>
        {opt.label} <window.Icon.chevDown size={10} />
      </button>
      {open && (
        <div className="mer-status-menu" style={{ right: 0, left: 'auto' }}>
          {OPP_STATUS.map((s) => (
            <div key={s.v} className="mer-status-opt"
                 onClick={() => { onChange(s.v); setOpen(false); }}>
              <span>{s.label}</span>
              {s.v === value && <window.Icon.check size={12} />}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Onglet Mission ──────────────────────────────────────────────
function TabMission({ opp, org, contact }) {
  const rows = [
    ['Organisation', org ? org.name : '—'],
    ['Type prospect', PROSPECT_LABELS[opp.prospect_type] || opp.prospect_type || '—'],
    ['Contact', contact ? contact.display_name : '—'],
    ['TJM', fmtTjm(opp.tjm)],
    ['Stack', opp.stack || '—'],
    ['Remote', opp.remote_policy || '—'],
    ['Jours / semaine', opp.jours_semaine || '—'],
    ['Durée', opp.duree || '—'],
    ['Démarrage', opp.date_demarrage || '—'],
    ['Canal', opp.channel || '—'],
  ];
  return (
    <div className="opp-mission">
      <div className="opp-field-grid">
        {rows.map(([k, v]) => (
          <div key={k} className="opp-field">
            <div className="opp-field-k">{k}</div>
            <div className="opp-field-v">{v}</div>
          </div>
        ))}
      </div>
      {contact && contact.linkedin_url && (
        <a className="btn ghost" href={`https://${contact.linkedin_url.replace(/^https?:\/\//, '')}`}
           target="_blank" rel="noreferrer" style={{ marginTop: 12, alignSelf: 'flex-start' }}>
          <window.Icon.link size={13} /> Profil LinkedIn du contact
        </a>
      )}
    </div>
  );
}

// ─── Onglet Candidats (shortlist devs go/maybe/no_go) ────────────
function TabCandidats({ opp, candidates, contacts, placements, onToast }) {
  const contactById = React.useMemo(() => {
    const m = {}; contacts.forEach((c) => { m[c.id] = c; }); return m;
  }, [contacts]);
  const placedContactIds = new Set(placements.map((p) => p.contact_id));

  // Tri : go d'abord, puis maybe, puis le reste, no_go en dernier.
  const order = { go: 0, maybe: 1, call_done: 2, contacted: 3, sourced: 4, no_go: 5 };
  const sorted = [...candidates].sort((a, b) =>
    (order[a.status] ?? 9) - (order[b.status] ?? 9) || (b.score || 0) - (a.score || 0));

  const updateStatus = async (cand, status) => {
    try {
      await window.Store.update('opportunity_candidates', 'opportunity_candidates', cand.id, { status });
    } catch (e) { onToast('Échec mise à jour candidat'); }
  };

  const promote = async (cand) => {
    try {
      await window.Store.promoteCandidate(opp.id, cand.id, {
        commission_rate_pct: 10,
        tjm: opp.tjm != null ? opp.tjm : null,
      });
      const c = contactById[cand.contact_id];
      onToast(`${c ? c.display_name : 'Candidat'} promu en placement ✓`);
    } catch (e) { onToast('Échec de la promotion'); }
  };

  if (candidates.length === 0) {
    return (
      <div className="empty" style={{ padding: '40px 24px' }}>
        <div className="mark"><window.Icon.user size={22} /></div>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, color: 'var(--brown)', marginBottom: 4 }}>
          Aucun dev en shortlist
        </div>
        <div className="tiny">Source des profils pour démarrer le matching sur cette opportunité.</div>
      </div>
    );
  }

  const goN = candidates.filter((c) => c.status === 'go').length;
  const maybeN = candidates.filter((c) => c.status === 'maybe').length;
  const nogoN = candidates.filter((c) => c.status === 'no_go').length;

  return (
    <div className="opp-cands">
      <div className="opp-cands-counts">
        <span className="cnt go"><i />{goN} GO</span>
        <span className="cnt maybe"><i />{maybeN} MAYBE</span>
        <span className="cnt nogo"><i />{nogoN} NO GO</span>
        <span className="cnt total">· {candidates.length} sourcés</span>
      </div>
      <table className="mer-table">
        <thead>
          <tr>
            <th>Dev</th>
            <th style={{ width: 110 }}>Heure call</th>
            <th style={{ width: 70 }}>Score</th>
            <th style={{ width: 130 }}>Statut</th>
            <th>Commentaire</th>
            <th style={{ width: 120 }} />
          </tr>
        </thead>
        <tbody>
          {sorted.map((cand) => {
            const c = contactById[cand.contact_id];
            const isPlaced = placedContactIds.has(cand.contact_id);
            return (
              <tr key={cand.id} className="mer-row">
                <td className="mer-row-presta" style={{ cursor: 'default' }}>
                  <window.Avatar name={c ? c.display_name : '?'} size="xs" />
                  <div className="vstack" style={{ gap: 1, minWidth: 0 }}>
                    <span className="nm">{c ? c.display_name : '—'}</span>
                    <span className="muted tiny" style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {c ? c.headline : ''}
                    </span>
                  </div>
                </td>
                <td className="muted tiny">{cand.call_time || '—'}</td>
                <td className="mono">{cand.score != null ? cand.score : '—'}</td>
                <td><CandStatusSelect value={cand.status} onChange={(v) => updateStatus(cand, v)} /></td>
                <td className="muted tiny">{cand.comment || '—'}</td>
                <td>
                  {isPlaced ? (
                    <span className="chip sm turq">Placé</span>
                  ) : (
                    <button className="btn primary" style={{ fontSize: 11, padding: '4px 9px' }}
                            onClick={() => promote(cand)}>
                      <window.Icon.check size={11} /> Promouvoir
                    </button>
                  )}
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

function CandStatusSelect({ value, onChange }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  const opt = CAND_STATUS_MAP[value] || CAND_STATUS[0];
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button className={`chip sm ${opt.cls}`} onClick={() => setOpen((o) => !o)} style={{ cursor: 'pointer' }}>
        {opt.label} <window.Icon.chevDown size={10} />
      </button>
      {open && (
        <div className="mer-status-menu">
          {CAND_STATUS.map((s) => (
            <div key={s.v} className="mer-status-opt"
                 onClick={() => { onChange(s.v); setOpen(false); }}>
              <span>{s.label}</span>
              {s.v === value && <window.Icon.check size={12} />}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Onglet Placement ────────────────────────────────────────────
function TabPlacement({ placements, contacts }) {
  const contactById = React.useMemo(() => {
    const m = {}; contacts.forEach((c) => { m[c.id] = c; }); return m;
  }, [contacts]);
  if (placements.length === 0) {
    return <TabPlaceholder label="Placement"
      text="Aucun placement. Promeus un candidat depuis l'onglet Candidats pour créer le placement." />;
  }
  return (
    <div className="opp-placements">
      {placements.map((p) => {
        const c = contactById[p.contact_id];
        const rows = [
          ['Dev placé', c ? c.display_name : '—'],
          ['TJM', fmtTjm(p.tjm)],
          ['Commission', p.commission_rate_pct != null ? `${p.commission_rate_pct} %` : (p.commission_daily_eur != null ? `${p.commission_daily_eur} €/j` : '—')],
          ['Commission €/j', p.commission_daily_eur != null ? `${p.commission_daily_eur} €` : '—'],
          ['Début mission', p.mission_start || '—'],
          ['Fin mission', p.mission_end || '—'],
          ['Statut', p.status || '—'],
        ];
        return (
          <div key={p.id} className="opp-placement-card">
            <div className="opp-field-grid">
              {rows.map(([k, v]) => (
                <div key={k} className="opp-field">
                  <div className="opp-field-k">{k}</div>
                  <div className="opp-field-v">{v}</div>
                </div>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─── Onglet placeholder générique ────────────────────────────────
function TabPlaceholder({ label, text }) {
  return (
    <div className="empty" style={{ padding: '48px 24px' }}>
      <div className="mark"><window.Icon.file size={22} /></div>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, color: 'var(--brown)', marginBottom: 4 }}>
        {label}
      </div>
      <div className="tiny" style={{ maxWidth: 340 }}>{text}</div>
    </div>
  );
}

window.OpportunitesView = OpportunitesView;
