/* global React, ReactDOM, YagodaStore, YComps, YDrawer, YPages1, YPages2 */
const { useState, useEffect, useMemo, useCallback } = React;
const Y = window.YagodaStore;
const { ToastProvider, useToast, TopBar, Sidebar } = window.YComps;
const { RunDrawer } = window.YDrawer;
const { OverviewPage, RunsPage, CalibrationPage, BenchmarksPage, SnapshotsPage, AlertsPage } = window.YPages1;
const { SettingsPage } = window.YPages2;

// In-browser test suite mirroring backend/tests/*
function buildTests() {
  const tests = [];
  const t = (name, fn) => tests.push({ name, fn });

  t('importer · qcodes record normalises to internal Run', () => {
    const r = Y.qcodesImporter({ run_id:'TEST-1', exp_name:'Rabi', sample_name:'Q1', captured_instruments:true, dataset_path:'/d/1', timestamp:'2026-05-02T00:00:00Z' });
    if (r.framework !== 'QCoDeS') throw new Error('framework');
    if (r.experiment !== 'Rabi') throw new Error('experiment');
    if (r.instrument_state !== 'captured') throw new Error('instrument_state');
  });

  t('importer · quantify record normalises with TUID', () => {
    const r = Y.quantifyImporter({ tuid:'20260502-120000-1234-abcd', schedule:'T1', qubit:'Q2', dataset_reference:'/d/2', timestamp:'2026-05-02T12:00:00Z' });
    if (r.framework !== 'Quantify') throw new Error('framework');
    if (r.metadata.tuid !== '20260502-120000-1234-abcd') throw new Error('tuid not preserved');
  });

  t('drift · stable when deviation ≤ 1.5%', () => {
    if (Y.deriveDriftState(0.8) !== 'stable') throw new Error('expected stable');
    if (Y.deriveDriftState(1.5) !== 'stable') throw new Error('boundary');
  });
  t('drift · warning when 1.5 < deviation ≤ 3.5%', () => {
    if (Y.deriveDriftState(2.0) !== 'warning') throw new Error('expected warning');
    if (Y.deriveDriftState(3.5) !== 'warning') throw new Error('boundary');
  });
  t('drift · unstable when deviation > 3.5%', () => {
    if (Y.deriveDriftState(4.8) !== 'unstable') throw new Error('expected unstable');
  });

  t('benchmark · regression when delta < -3.5', () => {
    if (Y.deriveBenchmarkStatus(-3.7) !== 'regression') throw new Error('expected regression');
    if (Y.deriveBenchmarkStatus(-1.0) !== 'stable') throw new Error('expected stable');
  });

  t('alerts · drift > 2.5% generates alert', () => {
    const run = { run_id:'X1', deviation:3.7, benchmark_previous:98, benchmark_current:97.5, instrument_state:'captured', experiment:'T1', sample:'Q', timestamp:'2026-05-02T00:00:00Z' };
    const alerts = Y.generateAlertsForRun(run);
    if (!alerts.find(a => a.alert_type==='drift')) throw new Error('no drift alert');
  });
  t('alerts · benchmark delta < -2.5 generates alert', () => {
    const run = { run_id:'X2', deviation:0.5, benchmark_previous:98, benchmark_current:94, instrument_state:'captured', experiment:'T1', sample:'Q', timestamp:'2026-05-02T00:00:00Z' };
    const alerts = Y.generateAlertsForRun(run);
    if (!alerts.find(a => a.alert_type==='benchmark')) throw new Error('no benchmark alert');
  });
  t('alerts · missing instrument_state generates alert', () => {
    const run = { run_id:'X3', deviation:0.5, benchmark_previous:98, benchmark_current:97.9, instrument_state:'missing', experiment:'T1', sample:'Q', timestamp:'2026-05-02T00:00:00Z' };
    const alerts = Y.generateAlertsForRun(run);
    if (!alerts.find(a => a.alert_type==='instrument_state')) throw new Error('no instrument alert');
  });

  t('snapshot · contains all required fields', () => {
    const run = Y.buildEnrichedRuns([Y.SEED_RUNS[0]])[0];
    const s = Y.buildSnapshot(run);
    const required = ['snapshot_id','run_id','framework','dataset_reference','instrument_state','calibration_state','benchmark_state','parameters_captured','calibration_metrics','software_version','timestamp','generated_by'];
    required.forEach(k => { if (!(k in s)) throw new Error('missing '+k); });
    if (s.generated_by !== 'QuantumLog Yagoda MVP') throw new Error('generated_by');
  });

  t('snapshot · completeness penalised when instrument missing', () => {
    const stable = Y.buildEnrichedRuns([{...Y.SEED_RUNS[0]}])[0];
    const broken = Y.buildEnrichedRuns([{...Y.SEED_RUNS[0], instrument_state:'missing'}])[0];
    if (broken.snapshot_completeness >= stable.snapshot_completeness) throw new Error('completeness should drop');
  });

  t('seed · 12 runs loaded with required fields', () => {
    const runs = Y.buildEnrichedRuns(Y.SEED_RUNS);
    if (runs.length !== 12) throw new Error('expected 12 runs');
    runs.forEach(r => {
      if (!r.calibration_metrics_json) throw new Error('missing calibration_metrics_json');
      if (!('snapshot_completeness' in r)) throw new Error('missing snapshot_completeness');
    });
  });

  t('compare · changed parameters and calibration diff are produced', () => {
    const runs = Y.buildEnrichedRuns(Y.SEED_RUNS);
    const cmp = Y.compareRuns(runs[3], runs[0]);
    if (!cmp) throw new Error('no comparison');
    if (!Array.isArray(cmp.calibration_diff)) throw new Error('calibration_diff missing');
    if (typeof cmp.benchmark_movement !== 'number') throw new Error('benchmark_movement');
  });

  t('empty · zero runs returns no alerts and empty arrays', () => {
    const alerts = Y.regenerateAlerts([]);
    if (alerts.length !== 0) throw new Error('expected empty');
  });

  return tests;
}
function runAllTests() {
  const tests = buildTests();
  return tests.map(t => {
    try { t.fn(); return { name: t.name, pass: true }; }
    catch (e) { return { name: t.name, pass: false, error: e.message }; }
  });
}



// =========== Strict private demo password gate ===========
const YAGODA_DEMO_PASSWORD = 'yagoda-demo';

function LoginGate({ children }) {
  const [ok, setOk] = React.useState(false);
  const [password, setPassword] = React.useState('');
  const [err, setErr] = React.useState('');

  if (ok) return children;

  const submit = (e) => {
    e.preventDefault();
    if (password.trim() === YAGODA_DEMO_PASSWORD) {
      setOk(true);
    } else {
      setErr('Access denied. Please check the password.');
    }
  };

  return (
    <div className="demo-password-screen">
      <form className="demo-password-card" onSubmit={submit}>
        <img src="assets/yagoda-icon.png" alt="" className="demo-password-icon"/>
        <div className="profile-kicker">Private Yagoda demo</div>
        <h1>Research console access</h1>
        <p>This demo uses sample data and is intended for selected researchers, labs, partners, and reviewers.</p>
        <label>Password</label>
        <input
          type="password"
          value={password}
          onChange={e => setPassword(e.target.value)}
          placeholder="Enter password"
          autoFocus
        />
        {err && <div className="demo-password-error">{err}</div>}
        <button className="btn btn-primary" type="submit">Open demo</button>
      </form>
    </div>
  );
}



// =========== Project profile modal ===========
const YAGODA_PROJECT_TEMPLATES = {
  quantum: {
    id: 'quantum',
    name: 'Quantum calibration workflow',
    field: 'Quantum / photonics',
    atlasMeaning: 'runs, qubits, readout channels, calibration stages',
    framework: 'Quantify',
    experiment: 'Readout Calibration',
    sample: 'Qubit-F06',
    sweep: 'readout frequency / amplitude',
    connectors: ['qcodes','quantify','qiskit','hdf5','json','instrument_logs']
  },
  engineering: {
    id: 'engineering',
    name: 'Engineering simulation workflow',
    field: 'Automotive / engineering',
    atlasMeaning: 'simulation runs, model versions, parameter sets, solver outputs',
    framework: 'MATLAB / Simulink',
    experiment: 'Controller Simulation',
    sample: 'Vehicle-Model-A',
    sweep: 'solver tolerance / brake response',
    connectors: ['matlab','simulink','csv','json','hdf5','hpc']
  },
  biomedical: {
    id: 'biomedical',
    name: 'Biomedical analysis workflow',
    field: 'Biomedical research',
    atlasMeaning: 'analysis runs, dataset versions, feature sets, reproducibility packets',
    framework: 'Python / R',
    experiment: 'Analysis Pipeline',
    sample: 'Dataset-A',
    sweep: 'feature threshold / model parameters',
    connectors: ['python','r','csv','tsv','matlab','json']
  },
  materials: {
    id: 'materials',
    name: 'Materials characterization workflow',
    field: 'Materials science',
    atlasMeaning: 'sample batches, process steps, microscopy/spectroscopy outputs',
    framework: 'Python / HDF5',
    experiment: 'Characterization Batch',
    sample: 'Sample-Batch-A',
    sweep: 'temperature / exposure / measurement set',
    connectors: ['python','hdf5','csv','instrument_logs','custom']
  },
  chemistry: {
    id: 'chemistry',
    name: 'Chemistry reproducibility workflow',
    field: 'Chemistry / lab workflow',
    atlasMeaning: 'experiments, samples, method versions, instrument outputs',
    framework: 'ELN / LIMS',
    experiment: 'Method Reproducibility',
    sample: 'Batch-A',
    sweep: 'method version / preparation condition',
    connectors: ['eln_lims','csv','json','instrument_logs','custom']
  },
  earth: {
    id: 'earth',
    name: 'Earth and space data pipeline',
    field: 'Earth / space science',
    atlasMeaning: 'dataset versions, pipeline steps, model configs, output products',
    framework: 'Python / NetCDF',
    experiment: 'Processing Pipeline',
    sample: 'Region-01',
    sweep: 'dataset version / processing configuration',
    connectors: ['python','netcdf','hdf5','hpc','json']
  }
};

function saveProjectProfile(profile) {
  localStorage.setItem('yagoda_project_profile', JSON.stringify(profile));
}

function applyProjectProfileToStore(store, profile) {
  const connectors = {...(store.settings?.connectors || {})};
  [
    'qcodes','quantify','qiskit','qibolab','qibocal','laboneq',
    'matlab','simulink','python','r','csv','tsv','json','hdf5','netcdf',
    'eln_lims','instrument_logs','hpc','custom','custom_scripts'
  ].forEach(k => { connectors[k] = !!profile.connectors?.includes(k); });

  const runs = (store.runs || []).map((r, i) => ({
    ...r,
    framework: profile.framework,
    experiment: profile.experiment,
    sample: profile.sample,
    parameter_sweep: profile.sweep,
    metadata: {
      ...(r.metadata || {}),
      project_profile: profile.id,
      project_field: profile.field,
      atlas_meaning: profile.atlasMeaning,
      adapted_sample_data: true,
      workflow_index: i + 1
    }
  }));

  return {
    ...store,
    runs,
    alerts: Y.regenerateAlerts(runs),
    settings: {
      ...store.settings,
      connectors,
      dataSources: {
        ...(store.settings?.dataSources || {}),
        dataset_directory: profile.id === 'quantum' ? '/data/lab/2026' : `/data/${profile.id}/project`,
        run_metadata: profile.id === 'quantum' ? '/data/lab/2026/metadata' : `/data/${profile.id}/metadata`,
        instrument_state: profile.id === 'quantum' ? '/data/lab/station_snapshots' : `/data/${profile.id}/context`,
        calibration_logs: profile.id === 'quantum' ? '/data/lab/calibration' : `/data/${profile.id}/comparison`,
        environment_notes: profile.id === 'quantum' ? '/data/lab/notes' : `/data/${profile.id}/notes`,
      }
    },
    meta: {
      ...(store.meta || {}),
      project_profile: profile,
      last_sync: new Date().toISOString()
    }
  };
}

function ProjectProfileModal({ onApply, onClose }) {
  const [selected, setSelected] = React.useState('quantum');
  const profile = YAGODA_PROJECT_TEMPLATES[selected] || YAGODA_PROJECT_TEMPLATES.quantum;
  const templates = Object.values(YAGODA_PROJECT_TEMPLATES);

  return (
    <div className="profile-modal-screen">
      <div className="profile-modal">
        <div className="profile-modal-head">
          <div className="profile-modal-title">
            <div className="profile-modal-icon-shell">
              <img src="assets/yagoda-icon.png" alt="" className="profile-modal-icon"/>
              <span className="profile-modal-version">v0.1</span>
            </div>
            <div>
              <div className="profile-kicker">Create / select project</div>
              <h2>Choose the research workflow for this demo.</h2>
              <p>Yagoda will adapt the sample console language, connectors, local paths, and workflow labels to match the type of research project being reviewed.</p>
            </div>
          </div>
          <button className="btn btn-ghost profile-open-console" onClick={onClose}>Open current console →</button>
        </div>

        <div className="profile-modal-grid">
          <div className="profile-template-list">
            {templates.map(t => (
              <button key={t.id} className={`profile-template ${selected===t.id?'active':''}`} onClick={() => setSelected(t.id)}>
                <strong>{t.name}</strong>
                <span>{t.field}</span>
              </button>
            ))}
          </div>

          <div className="profile-preview">
            <div className="profile-kicker">Atlas meaning</div>
            <h3>{profile.name}</h3>
            <div className="profile-row"><span>Field</span><b>{profile.field}</b></div>
            <div className="profile-row"><span>Atlas represents</span><b>{profile.atlasMeaning}</b></div>
            <div className="profile-row"><span>Framework label</span><b>{profile.framework}</b></div>
            <div className="profile-row"><span>Workflow</span><b>{profile.experiment}</b></div>
            <div className="profile-row"><span>Sample / object</span><b>{profile.sample}</b></div>
            <div className="profile-row"><span>Connectors</span><b>{profile.connectors.join(', ')}</b></div>
            <p className="profile-note">The visual system stays the same. The meaning and connectors change so an engineering, biomedical, chemistry, materials, or Earth science researcher does not see only Quantify language.</p>
          </div>
        </div>

        <div className="profile-modal-actions">
          <button className="btn btn-primary" onClick={() => onApply(profile)}>Apply project profile</button>
          <button className="btn btn-ghost" onClick={() => onApply(YAGODA_PROJECT_TEMPLATES.quantum)}>Use quantum sample</button>
        </div>
      </div>
    </div>
  );
}

// =========== App shell ===========
function AppInner() {
  const toast = useToast();
  const [store, setStoreState] = useState(() => Y.loadStore());
  const [page, setPage] = useState('overview');
  const [openRunId, setOpenRunId] = useState(null);
  const [showProjectProfile, setShowProjectProfile] = useState(true);

  const setStore = useCallback((updater) => {
    setStoreState(prev => {
      const next = typeof updater === 'function' ? updater(prev) : updater;
      Y.saveStore(next);
      return next;
    });
  }, []);

  const openRun = (runId) => {
    setOpenRunId(runId);
  };
  const closeRun = () => setOpenRunId(null);

  const generateSnapshot = (run) => {
    try {
      const snap = Y.buildSnapshot(run);
      setStore(prev => ({ ...prev, snapshots: [...prev.snapshots, snap] }));
      toast.push('Snapshot captured', `${snap.snapshot_id} · ${run.run_id} · completeness ${snap.snapshot_completeness}%`, 'success');
    } catch (e) {
      toast.push('Snapshot failed', e.message, 'error');
    }
  };

  const resetSample = () => {
    const fresh = Y.resetStore();
    const profile = getSavedProjectProfile();
    const next = profile ? applyProjectProfileToStore(fresh, profile) : fresh;
    Y.saveStore(next);
    setStoreState(next);
    toast.push('Sample reloaded', profile ? `12 seeded runs · ${profile.name}` : '12 seeded runs · alerts regenerated', 'success');
  };

  const applyProjectProfile = (profile) => {
    saveProjectProfile(profile);
    setStore(prev => applyProjectProfileToStore(prev, profile));
    setShowProjectProfile(false);
    toast.push('Project profile applied', `${profile.name} · ${profile.framework}`, 'success');
  };

  const onTagChange = (runId, tags) => {
    setStore(prev => {
      const runs = prev.runs.map(r => r.run_id===runId ? {...r, tags} : r);
      return { ...prev, runs };
    });
    toast.push('Tags updated', `${runId}: ${tags.join(', ') || 'cleared'}`, 'info');
  };

  const onCompare = () => {};

  const openRun_ = useMemo(() => store.runs.find(r => r.run_id === openRunId) || null, [store, openRunId]);
  const unresolved = store.alerts.filter(a => !a.resolved).length;

  return (
    <div className="app">
      <Sidebar page={page} setPage={(p)=>{ setPage(p); }} alertCount={unresolved} lastRun={store.runs[store.runs.length-1]}/>
      <TopBar store={store} lastSync={store.meta.last_sync}/>
      <main className="main">
        {page==='overview'    && <OverviewPage     store={store} setStore={setStore} openRun={openRun} generateSnapshot={generateSnapshot} resetSample={resetSample}/>}
        {page==='runs'        && <RunsPage         store={store} openRun={openRun} generateSnapshot={generateSnapshot}/>}
        {page==='calibration' && <CalibrationPage  store={store} openRun={openRun}/>}
        {page==='benchmarks'  && <BenchmarksPage   store={store}/>}
        {page==='snapshots'   && <SnapshotsPage    store={store} generateSnapshot={generateSnapshot} openRun={openRun}/>}
        {page==='alerts'      && <AlertsPage       store={store} setStore={setStore} openRun={openRun}/>}
        {page==='settings'    && <SettingsPage     store={store} setStore={setStore} resetSample={resetSample} runTests={runAllTests}/>}
      </main>
      {showProjectProfile && <ProjectProfileModal onApply={applyProjectProfile} onClose={() => setShowProjectProfile(false)}/>} 
      {openRun_ && <RunDrawer run={openRun_} runs={store.runs} snapshots={store.snapshots} alerts={store.alerts} onClose={closeRun} onSnapshot={generateSnapshot} onTagChange={onTagChange} onCompare={onCompare}/>}
    </div>
  );
}

function App() {
  return <ToastProvider><AppInner/></ToastProvider>;
}

ReactDOM.createRoot(document.getElementById('root')).render(<LoginGate><App/></LoginGate>);
