
const { useState, useEffect, useMemo } = React;

const API_BASE = 'https://transfer-lens.onrender.com';

// ── Data fetching hook ────────────────────────────────────────────────────────
function useApi(path) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!path) { setData(null); setLoading(false); setError(null); return; }
    let cancelled = false;
    setLoading(true);
    setError(null);
    fetch(API_BASE + path)
      .then(r => r.ok ? r.json() : Promise.reject(`HTTP ${r.status}`))
      .then(d => { if (!cancelled) { setData(d); setLoading(false); } })
      .catch(e => { if (!cancelled) { setError(String(e)); setLoading(false); } });
    return () => { cancelled = true; };
  }, [path]);

  return { data, loading, error };
}

// ── Shared loading / empty states ─────────────────────────────────────────────
function Spinner() {
  return (
    <div style={{ padding: '28px', textAlign: 'center', color: 'var(--text-3)' }}>
      <svg width="22" height="22" viewBox="0 0 22 22" fill="none"
        style={{ animation: 'pulse 1.1s ease-in-out infinite', display: 'inline-block' }}>
        <circle cx="11" cy="11" r="8" stroke="var(--accent)" strokeWidth="2.5"
          strokeDasharray="38 14" strokeLinecap="round"/>
      </svg>
    </div>
  );
}

function EmptyState({ message }) {
  return (
    <div style={{ padding: '16px 0', fontSize: '0.85rem', color: 'var(--text-3)' }}>
      {message}
    </div>
  );
}

// ── Articulation row — handles Course and Series requirement types ─────────────
function ArticRow({ req, inPlan, unfit, onAddToPlan }) {
  const artic = req.articulation;
  const articulated = artic && artic.articulated;

  // Build left-side label from receiving_courses or series_name
  let ucLabel;
  if (req.type === 'Series' && req.series_name) {
    ucLabel = req.series_name;
  } else if (req.receiving_courses && req.receiving_courses.length > 0) {
    const rc = req.receiving_courses[0];
    ucLabel = `${rc.prefix} ${rc.number} – ${rc.title}`;
  } else {
    ucLabel = '—';
  }

  // Filter out empty course groups (groups with no sending courses)
  const nonEmptyGroups = artic
    ? artic.course_groups.filter(g => g.courses && g.courses.length > 0)
    : [];

  const hasIvcCourses = articulated && nonEmptyGroups.length > 0;

  return (
    <div className={`artic-row ${!hasIvcCourses ? 'no-artic' : ''}`}>
      {/* Left: receiving school course */}
      <div className="artic-uc-side">
        <div className="artic-uc-course">{ucLabel}</div>
        {req.recommended ? (
          <span className="artic-rec-badge" title={req.attribute_note || ''}>
            Recommended
          </span>
        ) : null}
        <div className={`artic-status-dot ${hasIvcCourses ? 'ok' : 'none'}`}></div>
      </div>

      {/* Arrow */}
      <div className="artic-arrow">
        {hasIvcCourses
          ? <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
              <path d="M4 10h12M12 6l4 4-4 4" stroke="var(--green)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
            </svg>
          : <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
              <path d="M6 6l8 8M14 6l-8 8" stroke="var(--red)" strokeWidth="1.8" strokeLinecap="round"/>
            </svg>
        }
      </div>

      {/* Right: IVC sending courses */}
      <div className="artic-ivc-side">
        {hasIvcCourses ? (
          nonEmptyGroups.map((group, gi) => (
            <div key={gi} className="artic-ivc-group">
              {gi > 0 && (
                <span className="artic-or-badge">
                  {(nonEmptyGroups[gi].group_conjunction || 'OR').toUpperCase()}
                </span>
              )}
              {group.courses.map((c, ci) => {
                const courseKey = `${c.prefix} ${c.number}`;
                const placed = inPlan.has(courseKey);
                return (
                  <div key={ci} className="artic-ivc-course">
                    {ci > 0 && (
                      <span className="artic-and-badge">
                        {(group.course_conjunction || 'AND').toUpperCase()}
                      </span>
                    )}
                    <div className="artic-ivc-chip">
                      <span className="artic-ivc-key">{courseKey}</span>
                      {c.title && <span className="artic-ivc-title">{c.title}</span>}
                      <span className="artic-ivc-units">{c.min_units}u</span>
                      {placed
                        ? <span className="artic-in-plan-badge">✓ In plan</span>
                        : unfit && unfit.has(courseKey)
                        ? <span className="artic-unfit-badge" title="The scheduler could not fit this required course before your transfer term — extend your timeline, enable summer, or raise your course load">Required · doesn't fit your timeframe</span>
                        : <button className="artic-add-btn" onClick={() => onAddToPlan(courseKey, c)}>+ Add to plan</button>
                      }
                    </div>
                  </div>
                );
              })}
            </div>
          ))
        ) : (
          <div className="artic-no-equiv">
            <span className="artic-no-badge">No IVC equivalent</span>
            <span className="artic-no-note">
              {artic?.no_articulation_reason || 'Complete at the receiving school'}
            </span>
          </div>
        )}
      </div>
    </div>
  );
}

// ── Pool grouping ─────────────────────────────────────────────────────────────
// Group a section's requirements into render blocks: runs of mandatory rows,
// plus one block per pool_id ("complete N from the following" option groups).
function buildPoolBlocks(reqs) {
  const blocks = [];
  const poolBlocks = {};
  for (const req of reqs) {
    if (req.pool_id) {
      let block = poolBlocks[req.pool_id];
      if (!block) {
        block = { pool: true, selectN: req.select_n, reqs: [] };
        poolBlocks[req.pool_id] = block;
        blocks.push(block);
      }
      block.reqs.push(req);
    } else {
      const last = blocks[blocks.length - 1];
      if (last && !last.pool) last.reqs.push(req);
      else blocks.push({ pool: false, reqs: [req] });
    }
  }
  return blocks;
}

// select_n >= 4 is a unit threshold; smaller values count rows
// (mirrors the planner's pool convention).
function poolLabel(selectN) {
  const n = selectN || 1;
  if (n >= 4) return `Complete ${n} units from the following`;
  return `Complete ${n} of the following`;
}

// ── Articulation view ─────────────────────────────────────────────────────────
function ArticulationView({ major, institution, data, inPlan, unfit, onAddToPlan, onBack }) {
  if (!data || !data.requirements) {
    return (
      <div className="artic-no-data">
        <div className="artic-no-data-icon">
          <svg width="40" height="40" viewBox="0 0 40 40" fill="none">
            <circle cx="20" cy="20" r="16" stroke="var(--divider)" strokeWidth="2"/>
            <path d="M20 12v9M20 26v2" stroke="var(--text-3)" strokeWidth="2" strokeLinecap="round"/>
          </svg>
        </div>
        <div className="artic-no-data-title">No articulation data yet</div>
        <div className="artic-no-data-sub">
          We're working on adding more school and major combinations. Check back soon, or try a different school.
        </div>
        <button className="btn-ghost" onClick={onBack}>← Choose a different school</button>
      </div>
    );
  }

  const { requirements, stats } = data;

  // Walk requirements in order, emitting section headers for 'Requirement' type
  // and ArticRows for 'Course'/'Series' types
  const sections = [];
  let currentSection = { header: null, reqs: [] };
  for (const req of requirements) {
    if (req.type === 'Requirement') {
      if (currentSection.reqs.length > 0 || currentSection.header) {
        sections.push(currentSection);
      }
      currentSection = { header: req.group_label, reqs: [] };
    } else {
      currentSection.reqs.push(req);
    }
  }
  if (currentSection.reqs.length > 0 || currentSection.header) {
    sections.push(currentSection);
  }
  // If no sections were created (all requirements have no 'Requirement' headers),
  // treat everything as one unlabeled section
  if (sections.length === 0) {
    sections.push({ header: null, reqs: requirements.filter(r => r.type !== 'Requirement') });
  }

  // Pull recommended rows out of every section so e.g. Berkeley's "STRONGLY
  // RECOMMENDED COURSES" group renders as its own section instead of being
  // flattened into "Required Preparation". Recommended rows are grouped by
  // their triggering string: an all-caps trigger is an ASSIST group heading
  // (shown title-cased), anything else is an attribute note (generic
  // "Recommended"). Pooled rows stay with their pool — a pool is already a
  // choice.
  const titleCase = (s) => s.toLowerCase().replace(/(^|\s|\/|-)\S/g, (c) => c.toUpperCase());
  const recSections = [];
  const recIndex = new Map(); // section title -> recSections entry
  const baseSections = [];
  for (const section of sections) {
    const rest = [];
    for (const req of section.reqs) {
      if (req.recommended && !req.pool_id) {
        const note = (req.attribute_note || '').trim();
        const isHeading = note && note === note.toUpperCase();
        const title = isHeading ? titleCase(note) : 'Recommended';
        let bucket = recIndex.get(title);
        if (!bucket) {
          bucket = { header: title, reqs: [] };
          recIndex.set(title, bucket);
          recSections.push(bucket);
        }
        bucket.reqs.push(req);
      } else {
        rest.push(req);
      }
    }
    if (rest.length > 0 || section.header) {
      baseSections.push({ header: section.header, reqs: rest });
    }
  }
  const renderSections = [...baseSections, ...recSections];

  return (
    <div className="artic-view">
      <button className="btn-back" onClick={onBack}>← Back to schools</button>
      <div className="artic-hero">
        <div className="artic-hero-school">{institution.name}</div>
        <div className="artic-hero-major">{major.label}</div>
        <div className="artic-hero-stats">
          <span className="artic-stat ok">{stats.articulated} of {stats.total} requirements articulated</span>
          {stats.not_articulated > 0 && (
            <span className="artic-stat missing">{stats.not_articulated} require on-campus completion</span>
          )}
        </div>
      </div>

      <div className="artic-legend">
        <span className="artic-legend-item"><span className="legend-dot ok"></span>Articulated</span>
        <span className="artic-legend-item"><span className="legend-dot none"></span>No IVC equivalent</span>
      </div>

      {renderSections.map((section, si) => (
        section.reqs.length > 0 && (
          <div key={si} className="artic-section">
            <div className="artic-section-header">
              <span className="artic-section-title">
                {section.header || (si === 0 ? 'Required Preparation' : 'Additional Requirements')}
              </span>
              <span className="artic-section-count">{section.reqs.length} course{section.reqs.length !== 1 ? 's' : ''}</span>
            </div>
            <div className="artic-rows">
              {buildPoolBlocks(section.reqs).map((block, bi) => (
                block.pool ? (
                  <div key={bi} className="artic-pool">
                    <div className="artic-pool-header">
                      <span className="artic-pool-label">{poolLabel(block.selectN)}</span>
                      <span className="artic-pool-count">{block.reqs.length} options</span>
                    </div>
                    <div className="artic-rows">
                      {block.reqs.map(req => (
                        <ArticRow key={req.id} req={req} inPlan={inPlan} unfit={unfit} onAddToPlan={onAddToPlan} />
                      ))}
                    </div>
                  </div>
                ) : (
                  <React.Fragment key={bi}>
                    {block.reqs.map(req => (
                      <ArticRow key={req.id} req={req} inPlan={inPlan} unfit={unfit} onAddToPlan={onAddToPlan} />
                    ))}
                  </React.Fragment>
                )
              ))}
            </div>
          </div>
        )
      ))}
    </div>
  );
}

// ── Transfer view ─────────────────────────────────────────────────────────────
function TransferView({ profile, onUpdateProfile }) {
  const [selectedSchool, setSelectedSchool] = useState(null);
  const [selectedMajor, setSelectedMajor] = useState(null); // { id, label }
  const [schoolQuery, setSchoolQuery] = useState('');
  const [majorQuery, setMajorQuery] = useState('');
  const [showArtic, setShowArtic] = useState(false);
  const [articPath, setArticPath] = useState(null);

  // Fetch UC and CSU schools separately, in parallel
  const { data: ucSchools,  loading: ucLoading  } = useApi('/api/institutions?category=UC');
  const { data: csuSchools, loading: csuLoading } = useApi('/api/institutions?category=CSU');

  // Fetch majors once a school is selected
  const majorApiPath = selectedSchool ? `/api/institutions/${selectedSchool.id}/majors` : null;
  const { data: majors, loading: majorsLoading } = useApi(majorApiPath);

  // Fetch articulation only when the user clicks the CTA
  const { data: articData, loading: articLoading } = useApi(articPath);

  const inPlan = useMemo(() => {
    const keys = new Set(Object.values(profile.semesters || {}).flat());
    Object.keys(profile.courseStatuses || {}).forEach(k => keys.add(k));
    return keys;
  }, [profile]);

  // Required courses the scheduler couldn't fit before the transfer term —
  // their rows warn instead of offering "+ Add to plan". A course the user
  // manually placed is no longer unfit.
  const unfit = useMemo(() => {
    return new Set((profile.requiredUnplaced || []).filter(k => !inPlan.has(k)));
  }, [profile.requiredUnplaced, inPlan]);

  const handleAddToPlan = (courseKey, courseData) => {
    // Inject into MOCK_COURSES so CourseCard can render it
    if (!window.MOCK_COURSES[courseKey] && courseData) {
      window.MOCK_COURSES[courseKey] = {
        title: courseData.title,
        units: courseData.min_units || 3,
        difficulty: 'medium',
        available: 'FS',
        prereqs: [],
      };
    }
    const sems = { ...profile.semesters };
    const semOrder = Object.keys(sems).sort();
    for (const sid of semOrder) {
      const courses = sems[sid] || [];
      if (courses.length < 5 && !courses.includes(courseKey)) {
        sems[sid] = [...courses, courseKey];
        onUpdateProfile({ semesters: sems });
        return;
      }
    }
  };

  const filterSchools = (schools) => {
    if (!schools) return [];
    if (!schoolQuery) return schools;
    const q = schoolQuery.toLowerCase();
    return schools.filter(s =>
      s.name.toLowerCase().includes(q) || s.code.toLowerCase().includes(q)
    );
  };

  const filteredMajors = useMemo(() => {
    if (!majors) return [];
    if (!majorQuery) return majors;
    // Whitespace-normalized: ASSIST labels can carry doubled spaces.
    const norm = s => s.toLowerCase().replace(/\s+/g, ' ').trim();
    const q = norm(majorQuery);
    return majors.filter(m => norm(m.label).includes(q));
  }, [majors, majorQuery]);

  const handleSelectSchool = (school) => {
    setSelectedSchool(school);
    setSelectedMajor(null);
    setMajorQuery('');
    setShowArtic(false);
    setArticPath(null);
  };

  const handleShowArtic = () => {
    setArticPath(`/api/majors/${selectedMajor.id}/articulation`);
    setShowArtic(true);
  };

  const handleBackToSchools = () => {
    setShowArtic(false);
    setSelectedMajor(null);
    setArticPath(null);
  };

  // Articulation screen
  if (showArtic && selectedSchool && selectedMajor) {
    return (
      <div className="transfer-view">
        {articLoading ? (
          <>
            <button className="btn-back" onClick={handleBackToSchools}>← Back to schools</button>
            <Spinner />
          </>
        ) : (
          <ArticulationView
            major={selectedMajor}
            institution={selectedSchool}
            data={articData}
            inPlan={inPlan}
            unfit={unfit}
            onAddToPlan={handleAddToPlan}
            onBack={handleBackToSchools}
          />
        )}
      </div>
    );
  }

  const schoolsLoading = ucLoading || csuLoading;
  const filteredUC  = filterSchools(ucSchools  || []);
  const filteredCSU = filterSchools(csuSchools || []);
  const noResults   = !schoolsLoading && filteredUC.length === 0 && filteredCSU.length === 0;

  return (
    <div className="transfer-view">
      <div className="transfer-intro">
        <h2 className="transfer-title">Transfer Requirements</h2>
        <p className="transfer-sub">Select a school and major to see which IVC courses satisfy each requirement.</p>
      </div>

      {/* ── Step 1: School ── */}
      <div className="transfer-step">
        <div className="transfer-step-label">1 · Target School</div>
        {selectedSchool ? (
          <div className="transfer-selected-row">
            <span className="transfer-selected-name">{selectedSchool.name}</span>
            <button className="btn-ghost-sm" onClick={() => handleSelectSchool(null)}>Change</button>
          </div>
        ) : (
          <>
            <div className="wiz-search-wrap" style={{ marginBottom: 12 }}>
              <svg className="wiz-search-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
                <circle cx="6.5" cy="6.5" r="4" stroke="currentColor" strokeWidth="1.5"/>
                <path d="M10 10l3 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
              </svg>
              <input className="wiz-search" placeholder="Search schools…"
                value={schoolQuery} onChange={e => setSchoolQuery(e.target.value)} />
            </div>

            {schoolsLoading ? <Spinner /> : noResults ? (
              <EmptyState message={`No schools match "${schoolQuery}"`} />
            ) : (
              <div className="transfer-school-grid">
                {filteredUC.length > 0 && (
                  <>
                    <div style={{
                      width: '100%', fontSize: '0.7rem', fontWeight: 700,
                      letterSpacing: '0.06em', textTransform: 'uppercase',
                      color: 'var(--text-3)', marginBottom: 4,
                    }}>UC System</div>
                    {filteredUC.map(s => (
                      <button key={s.id} className="transfer-school-btn" onClick={() => handleSelectSchool(s)}>
                        <span className="transfer-school-abbr">{s.code}</span>
                        <span className="transfer-school-type">UC · {s.major_count} majors</span>
                      </button>
                    ))}
                  </>
                )}

                {filteredUC.length > 0 && filteredCSU.length > 0 && (
                  <div style={{ width: '100%', height: 8 }} />
                )}

                {filteredCSU.length > 0 && (
                  <>
                    <div style={{
                      width: '100%', fontSize: '0.7rem', fontWeight: 700,
                      letterSpacing: '0.06em', textTransform: 'uppercase',
                      color: 'var(--text-3)', marginBottom: 4,
                    }}>CSU System</div>
                    {filteredCSU.map(s => (
                      <button key={s.id} className="transfer-school-btn" onClick={() => handleSelectSchool(s)}>
                        <span className="transfer-school-abbr">{s.code}</span>
                        <span className="transfer-school-type">CSU · {s.major_count} majors</span>
                      </button>
                    ))}
                  </>
                )}
              </div>
            )}
          </>
        )}
      </div>

      {/* ── Step 2: Major ── */}
      {selectedSchool && (
        <div className="transfer-step">
          <div className="transfer-step-label">2 · Major</div>
          {selectedMajor ? (
            <div className="transfer-selected-row">
              <span className="transfer-selected-name">{selectedMajor.label}</span>
              <button className="btn-ghost-sm" onClick={() => setSelectedMajor(null)}>Change</button>
            </div>
          ) : (
            <>
              <div className="wiz-search-wrap" style={{ marginBottom: 12 }}>
                <svg className="wiz-search-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <circle cx="6.5" cy="6.5" r="4" stroke="currentColor" strokeWidth="1.5"/>
                  <path d="M10 10l3 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
                </svg>
                <input className="wiz-search" placeholder="Search majors…"
                  value={majorQuery} onChange={e => setMajorQuery(e.target.value)} autoFocus />
              </div>

              {majorsLoading ? <Spinner /> : filteredMajors.length === 0 ? (
                <EmptyState message={
                  majorQuery ? `No majors match "${majorQuery}"` : 'No majors available for this school'
                } />
              ) : (
                <div className="transfer-major-list">
                  {filteredMajors.map(m => (
                    <button key={m.id} className="transfer-major-btn" onClick={() => setSelectedMajor(m)}>
                      {m.label}
                      <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
                        <path d="M5 3l4 4-4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
                      </svg>
                    </button>
                  ))}
                </div>
              )}
            </>
          )}
        </div>
      )}

      {/* ── CTA ── */}
      {selectedSchool && selectedMajor && (
        <div className="transfer-cta">
          <button className="btn-primary-purple" onClick={handleShowArtic}>
            See articulation for {selectedSchool.code} · {selectedMajor.label} →
          </button>
        </div>
      )}
    </div>
  );
}

Object.assign(window, { TransferView, ArticulationView });
