const { useEffect, useMemo, useState, useRef } = React;

function useApiBase() {
  const params = new URLSearchParams(window.location.search);
  const override = params.get('api');
  if (override) return override.replace(/\/$/, '');
  return '';
}

function fetchJson(url) {
  return fetch(url).then((r) => {
    if (!r.ok) throw new Error(`HTTP ${r.status}`);
    return r.json();
  });
}

function labelizeCategory(s) {
  if (!s) return 'Other';
  return s.split('_').map((p) => (p ? (p[0].toUpperCase() + p.slice(1)) : p)).join(' ');
}

function App() {
  const apiBase = useApiBase();
  const [rules, setRules] = useState([]);
  const [concepts, setConcepts] = useState([]);
  const [selectedRuleIds, setSelectedRuleIds] = useState([]);
  const [selectedConceptCodes, setSelectedConceptCodes] = useState([]);
  const [careOptions, setCareOptions] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const [expandedIds, setExpandedIds] = useState(() => new Set());
  const [subgroupsState, setSubgroupsState] = useState({});

  const rulesSelectRef = useRef(null);
  const conceptsSelectRef = useRef(null);
  const rulesChoicesRef = useRef(null);
  const conceptsChoicesRef = useRef(null);

  const categoryOrder = [
    'diagnosis',
    'histology',
    'biomarkers',
    'interventions',
    'stage',
    'disease_descriptors',
    'screening_items',
    'demographics',
    'comorbidity',
    'clinical_score',
    'lab_values',
  ];

  const rulesByCategory = useMemo(() => {
    const grouped = {};
    for (const rule of rules) {
      const raw = (rule && typeof rule.category === 'string') ? rule.category.trim() : '';
      const cat = raw || 'other';
      if (!grouped[cat]) grouped[cat] = [];
      grouped[cat].push(rule);
    }
    return grouped;
  }, [rules]);

  const conceptsByCategory = useMemo(() => {
    const grouped = {};
    for (const concept of concepts) {
      const raw = (concept && typeof concept.category === 'string') ? concept.category.trim() : '';
      const cat = raw || 'other';
      if (!grouped[cat]) grouped[cat] = [];
      grouped[cat].push(concept);
    }
    return grouped;
  }, [concepts]);

  const orderedRuleCategories = useMemo(() => {
    const present = Object.keys(rulesByCategory);
    const preferred = categoryOrder.filter((c) => present.includes(c));
    const extras = present.filter((c) => !categoryOrder.includes(c)).sort();
    return [...preferred, ...extras];
  }, [rulesByCategory]);

  const orderedConceptCategories = useMemo(() => {
    const present = Object.keys(conceptsByCategory);
    const preferred = categoryOrder.filter((c) => present.includes(c));
    const extras = present.filter((c) => !categoryOrder.includes(c)).sort();
    return [...preferred, ...extras];
  }, [conceptsByCategory]);

  useEffect(() => {
    Promise.all([
      fetchJson(`${apiBase}/rules/?limit=500`),
      fetchJson(`${apiBase}/concepts/?limit=500`),
    ])
      .then(([rulesData, conceptsData]) => {
        setRules(rulesData);
        setConcepts(conceptsData);
      })
      .catch((e) => setError(e.message));
  }, [apiBase]);

  const queryString = useMemo(() => {
    const params = new URLSearchParams();
    if (selectedRuleIds.length) params.set('rule_ids', selectedRuleIds.join(','));
    if (selectedConceptCodes.length) params.set('concept_codes', selectedConceptCodes.join(','));
    params.set('limit', '50');
    return params.toString();
  }, [selectedRuleIds, selectedConceptCodes]);

  const loadCareOptions = () => {
    setLoading(true);
    setError(null);
    fetchJson(`${apiBase}/care-options/?${queryString}`)
      .then(setCareOptions)
      .catch((e) => setError(e.message))
      .finally(() => setLoading(false));
  };

  useEffect(() => {
    loadCareOptions();
  }, [queryString, apiBase]);

  useEffect(() => {
    if (!window.Choices) return;
    const el = rulesSelectRef.current;
    if (!el) return;

    if (rulesChoicesRef.current) {
      rulesChoicesRef.current.destroy();
      rulesChoicesRef.current = null;
    }

    const instance = new Choices(el, {
      removeItemButton: true,
      shouldSort: false,
      searchEnabled: true,
      placeholder: true,
      placeholderValue: 'Select rules',
    });
    rulesChoicesRef.current = instance;

    const onChange = (e) => {
      const values = Array.from(e.target.selectedOptions).map((o) => o.value);
      setSelectedRuleIds(values);
    };
    el.addEventListener('change', onChange);

    return () => {
      el.removeEventListener('change', onChange);
      if (rulesChoicesRef.current) {
        rulesChoicesRef.current.destroy();
        rulesChoicesRef.current = null;
      }
    };
  }, [rules]);

  useEffect(() => {
    if (!window.Choices) return;
    const el = conceptsSelectRef.current;
    if (!el) return;

    if (conceptsChoicesRef.current) {
      conceptsChoicesRef.current.destroy();
      conceptsChoicesRef.current = null;
    }

    const instance = new Choices(el, {
      removeItemButton: true,
      shouldSort: false,
      searchEnabled: true,
      placeholder: true,
      placeholderValue: 'Select concepts',
    });
    conceptsChoicesRef.current = instance;

    const onChange = (e) => {
      const values = Array.from(e.target.selectedOptions).map((o) => o.value);
      setSelectedConceptCodes(values);
    };
    el.addEventListener('change', onChange);

    return () => {
      el.removeEventListener('change', onChange);
      if (conceptsChoicesRef.current) {
        conceptsChoicesRef.current.destroy();
        conceptsChoicesRef.current = null;
      }
    };
  }, [concepts]);

  useEffect(() => {
    const inst = rulesChoicesRef.current;
    if (!inst) return;
    inst.removeActiveItems();
    if (selectedRuleIds.length) inst.setChoiceByValue(selectedRuleIds);
  }, [selectedRuleIds]);

  useEffect(() => {
    const inst = conceptsChoicesRef.current;
    if (!inst) return;
    inst.removeActiveItems();
    if (selectedConceptCodes.length) inst.setChoiceByValue(selectedConceptCodes);
  }, [selectedConceptCodes]);

  const resetFilters = () => {
    setSelectedRuleIds([]);
    setSelectedConceptCodes([]);
    setExpandedIds(new Set());
    setSubgroupsState({});
  };

  const toggleRow = (careOptionId) => {
    setExpandedIds((prev) => {
      const next = new Set(prev);
      if (next.has(careOptionId)) {
        next.delete(careOptionId);
      } else {
        next.add(careOptionId);
        if (!subgroupsState[careOptionId]) {
          setSubgroupsState((p) => ({ ...p, [careOptionId]: { loading: true, error: null, data: null } }));
          fetchJson(`${apiBase}/care-options/${encodeURIComponent(careOptionId)}/subgroups`)
            .then((rows) => {
              setSubgroupsState((p) => ({ ...p, [careOptionId]: { loading: false, error: null, data: rows || [] } }));
            })
            .catch((e) => {
              setSubgroupsState((p) => ({ ...p, [careOptionId]: { loading: false, error: String(e.message || e), data: [] } }));
            });
        }
      }
      return next;
    });
  };

  const renderSubgroupTable = (careOptionId) => {
    const st = subgroupsState[careOptionId];
    if (!st || st.loading) {
      return (
        <div className="status">Loading subgroups…</div>
      );
    }
    if (st.error) {
      return (
        <div className="status error">{st.error}</div>
      );
    }
    const rows = st.data || [];
    if (!rows.length) {
      return (
        <div className="status">No subgroups found</div>
      );
    }
    const getCatRules = (sg, cat) => {
      const rulesByCat = (sg.resolved_rules_by_category || {});
      return rulesByCat[cat] || [];
    };

    const labelize = (s) => s.split('_').map((p) => (p ? (p[0].toUpperCase() + p.slice(1)) : p)).join(' ');

    const categoryOrder = [
      'diagnosis',
      'histology',
      'biomarkers',
      'interventions',
      'stage',
      'disease_descriptors',
      'screening_items',
      'demographics',
      'comorbidity',
      'clinical_score',
      'lab_values',
    ];

    const present = new Set();
    rows.forEach((sg) => {
      const byCat = sg.resolved_rules_by_category || {};
      Object.keys(byCat).forEach((k) => {
        if (Array.isArray(byCat[k]) && byCat[k].length) present.add(k);
      });
    });
    const dynamicCategories = categoryOrder.filter((c) => present.has(c))
      .concat(Array.from(present).filter((c) => !categoryOrder.includes(c)));

    return (
      <div style={{ overflowX: 'auto', WebkitOverflowScrolling: 'touch' }}>
        <table style={{ width: 'max-content' }}>
          <thead>
            <tr>
              <th>Subgroup ID</th>
              <th>Name</th>
              <th>Line of therapy</th>
              {dynamicCategories.map((cat) => (
                <th key={cat}>{labelize(cat)}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {rows.map((sg) => (
              <tr key={sg.subgroup_id}>
                <td>{sg.subgroup_id}</td>
                <td>{sg.name || ''}</td>
                <td>{sg.line_of_therapy || ''}</td>
                {dynamicCategories.map((cat) => (
                  <td key={cat}>
                    <ul style={{ margin: 0, paddingLeft: '18px' }}>
                      {getCatRules(sg, cat).map((t, i) => (<li key={i}>{t}</li>))}
                    </ul>
                  </td>
                ))}
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
  };

  return (
    <div className="container">
      <header>
        <h1>Care Options</h1>
        <div className="api-info">
          <span>API:</span>
          <code>{apiBase || window.location.origin}</code>
        </div>
      </header>

      <div className="filter-actions">
        <button type="button" onClick={resetFilters}>Reset filters</button>
      </div>
      <section className="filters">
        <div className="filter">
          <label>Rules</label>
          <select
            multiple
            ref={rulesSelectRef}
          >
            {orderedRuleCategories.map((cat) => (
              <optgroup key={cat} label={labelizeCategory(cat === 'other' ? 'Other' : cat)}>
                {(rulesByCategory[cat] || []).map((r) => (
                  <option key={r.rule_id} value={r.rule_id}>
                    {r.display_name || r.rule_id}
                  </option>
                ))}
              </optgroup>
            ))}
          </select>
        </div>

        <div className="filter">
          <label>Concepts</label>
          <select
            multiple
            ref={conceptsSelectRef}
          >
            {orderedConceptCategories.map((cat) => (
              <optgroup key={cat} label={labelizeCategory(cat === 'other' ? 'Other' : cat)}>
                {(conceptsByCategory[cat] || []).map((c) => (
                  <option key={c.concept_code} value={c.concept_code}>
                    {c.concept_name || c.concept_code}
                  </option>
                ))}
              </optgroup>
            ))}
          </select>
        </div>
      </section>

      <section className="table-section">
        {loading ? (
          <div className="status">Loading…</div>
        ) : error ? (
          <div className="status error">{String(error)}</div>
        ) : (
          <table>
            <thead>
              <tr>
                <th>ID</th>
                <th>Type</th>
                <th>Title</th>
                <th>Sponsor</th>
                <th>Phase</th>
                <th>Domain</th>
              </tr>
            </thead>
            <tbody>
              {careOptions.map((co) => {
                const isExpanded = expandedIds.has(co.care_option_id);
                return (
                  React.createElement(React.Fragment, { key: co.care_option_id },
                    React.createElement('tr', { onClick: () => toggleRow(co.care_option_id), style: { cursor: 'pointer' } },
                      React.createElement('td', null, (isExpanded ? '▼ ' : '▶ '), co.care_option_id),
                      React.createElement('td', null, co.care_option_type || ''),
                      React.createElement('td', null, co.title || ''),
                      React.createElement('td', null, co.sponsor || ''),
                      React.createElement('td', null, co.phase || ''),
                      React.createElement('td', null, co.domain || '')
                    ),
                    isExpanded && (
                      React.createElement('tr', null,
                        React.createElement('td', { colSpan: 6 },
                          React.createElement('div', { style: { padding: '10px 0', maxWidth: '1120px' } },
                            renderSubgroupTable(co.care_option_id)
                          )
                        )
                      )
                    )
                  )
                );
              })}
            </tbody>
          </table>
        )}
      </section>

      <footer>
        <small>
          Tip: Add ?api=https://rest-api-bx7ihmp4ua-uc.a.run.app to use the remote API.
        </small>
      </footer>
    </div>
  );
}

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

