/* ===================================================
   DROPDOWN — custom select with glass styling
   props:
     - label: short header text
     - placeholder: when nothing selected
     - options: [{id, name, sub?}]
     - value: id (single) or array of ids (multi)
     - onChange: (newValue) => void
     - multi: bool
     - searchable: bool — show input on top
   =================================================== */
function Dropdown({ label, placeholder, options, value, onChange, multi = false, searchable = false }) {
  const [open, setOpen] = React.useState(false);
  const [query, setQuery] = React.useState('');
  const ref = React.useRef(null);

  React.useEffect(() => {
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    window.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      window.removeEventListener('keydown', onKey);
    };
  }, []);

  const isSelected = (id) => multi ? value.includes(id) : value === id;
  const handlePick = (id) => {
    if (multi) {
      onChange(value.includes(id) ? value.filter(x => x !== id) : [...value, id]);
    } else {
      onChange(id);
      setOpen(false);
    }
  };

  const filtered = searchable && query
    ? options.filter(o => o.name.toLowerCase().includes(query.toLowerCase()))
    : options;

  // Trigger label
  let triggerText = placeholder;
  if (multi) {
    triggerText = value.length > 0 ? `${value.length} seleccionada${value.length === 1 ? '' : 's'}` : placeholder;
  } else {
    const sel = options.find(o => o.id === value);
    if (sel) triggerText = sel.name;
  }

  return (
    <div className={"dropdown" + (open ? ' is-open' : '')} ref={ref}>
      <button
        type="button"
        className="dropdown__trigger"
        onClick={() => setOpen(o => !o)}
        aria-expanded={open}
      >
        <span className="dropdown__label">{label}</span>
        <span className="dropdown__value">{triggerText}</span>
        <span className="dropdown__caret" aria-hidden>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
            <polyline points="6 9 12 15 18 9"/>
          </svg>
        </span>
      </button>

      {open && (
        <div className="dropdown__panel" role="listbox">
          {searchable && (
            <div className="dropdown__search">
              <span className="search-icon"><Icon.Search /></span>
              <input
                type="text"
                placeholder="Buscar…"
                value={query}
                onChange={e => setQuery(e.target.value)}
                autoFocus
              />
            </div>
          )}
          <div className="dropdown__list">
            {filtered.length === 0 ? (
              <div className="dropdown__empty">Sin coincidencias</div>
            ) : filtered.map(opt => {
              const on = isSelected(opt.id);
              return (
                <button
                  key={opt.id}
                  type="button"
                  className={"dropdown__option" + (on ? ' is-selected' : '')}
                  onClick={() => handlePick(opt.id)}
                  role="option"
                  aria-selected={on}
                >
                  <span className="dropdown__option-name">{opt.name}</span>
                  {opt.sub && <span className="dropdown__option-sub">{opt.sub}</span>}
                  <span className={"dropdown__option-mark" + (multi ? ' is-multi' : '')}>
                    {on && (
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
                        <polyline points="20 6 9 17 4 12"/>
                      </svg>
                    )}
                  </span>
                </button>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

/* ===================================================
   PROJECT CARD
   =================================================== */
function ProjectCard({ project, authors, tags, onOpen, style }) {
  const projAuthors = project.author_ids.map(id => authors.find(a => a.id === id)).filter(Boolean);
  const projTags    = project.tag_ids.map(id => tags.find(t => t.id === id)).filter(Boolean);
  const visibleTags = projTags.slice(0, 3);
  const extraCount  = projTags.length - visibleTags.length;

  return (
    <button className="project-card" style={style} onClick={() => onOpen(project)}>
      <div className="project-card__cover">
        {project.image ? (
          <img src={project.image} alt={project.title} style={{ width: '100%', height: '100%', objectFit: 'cover' }} loading="lazy" />
        ) : (
          <span className="project-card__cover-label">{`image\n${project.id}.png`}</span>
        )}
      </div>
      <div className="project-card__body">
        <div className="project-card__head">
          <h3 className="project-card__title">{project.title}</h3>
          <span className="project-card__cat">
            {project.category === 'repo' ? 'repo' : 'proyecto'}
          </span>
        </div>
        <div className="project-card__authors">
          {projAuthors.map(a => (
            <img key={a.id} className="av" src={a.avatar} alt={a.name} loading="lazy" />
          ))}
        </div>
        <div className="project-card__footer">
          <div className="project-card__tags">
            {visibleTags.map(t => (
              <span key={t.id} className="chip chip--small">{t.name}</span>
            ))}
            {extraCount > 0 && (
              <span className="chip chip--small">+{extraCount}</span>
            )}
          </div>
          {project.live_url && (
            <a href={project.live_url} target="_blank" rel="noreferrer" className="project-card__live-link" onClick={e => e.stopPropagation()} title="Ver en vivo">
              <Icon.ExternalLink />
            </a>
          )}
        </div>
      </div>
    </button>
  );
}

/* ===================================================
   MODAL
   =================================================== */
function ProjectModal({ project, authors, tags, onClose }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => {
      window.removeEventListener('keydown', onKey);
      document.body.style.overflow = '';
    };
  }, [onClose]);

  if (!project) return null;
  const projAuthors = project.author_ids.map(id => authors.find(a => a.id === id)).filter(Boolean);
  const projTags    = project.tag_ids.map(id => tags.find(t => t.id === id)).filter(Boolean);

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <button className="modal__close" onClick={onClose} aria-label="Cerrar"><Icon.Close /></button>
        <div className="modal__cover">
          {project.image ? (
            <img src={project.image} alt={project.title} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
          ) : (
            `image · ${project.id}.png`
          )}
        </div>
        <div className="modal__body">
          <p className="modal__cat">{project.category === 'repo' ? 'Repositorio' : 'Proyecto'}</p>
          <h2 className="modal__title" style={ project['color-title'] ? { color: project['color-title'] } : {} }>{project.title}</h2>
          <div className="modal__links">
            {project.repo_url && (
              <a href={project.repo_url} target="_blank" rel="noreferrer" className="btn btn--ghost">
                <Icon.Github /> Repositorio <Icon.ExternalLink />
              </a>
            )}
            {project.live_url && (
              <a href={project.live_url} target="_blank" rel="noreferrer" className="btn btn--primary">
                <Icon.Globe /> En producción <Icon.ExternalLink />
              </a>
            )}
          </div>
          <p className="modal__desc">{project.description}</p>

          <div className="modal__section">
            <h4>Autores</h4>
            <div className="modal__authors">
              {projAuthors.map(a => (
                <a
                  key={a.id}
                  href={`/autor/${a.id}`}
                  className="modal-author-btn"
                  onClick={(e) => { e.preventDefault(); navigateWithTransition(`/autor/${a.id}`); }}
                >
                  <img src={a.avatar} alt={a.name} />
                  <span>{a.name}</span>
                </a>
              ))}
            </div>
          </div>

          <div className="modal__section">
            <h4>Etiquetas</h4>
            <div className="chip-list">
              {projTags.map(t => (
                <span key={t.id} className="chip">{t.name}</span>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ===================================================
   REPO APP
   =================================================== */
function RepoApp() {
  const { data: tags }     = useJson('/data/tags.json');
  const { data: authors }  = useJson('/data/authors.json');
  const { data: projects } = useJson('/data/projects.json');

  const [query, setQuery]      = React.useState('');
  const [category, setCategory]= React.useState('all'); // all | project | repo
  const [tagFilter, setTagFilter]       = React.useState([]); // ids
  const [authorFilter, setAuthorFilter] = React.useState([]); // ids
  const [open, setOpen] = React.useState(null);

  const [gridFade, setGridFade] = React.useState(false);
  const gridRef = React.useRef(null);
  // key that forces cards to re-animate when filters change
  const [animKey, setAnimKey] = React.useState(0);

  const toggle = (list, setter, value) => {
    setter(list.includes(value) ? list.filter(x => x !== value) : [...list, value]);
  };

  const filtered = React.useMemo(() => {
    if (!projects) return [];
    return projects.filter(p => {
      if (query && !p.title.toLowerCase().includes(query.toLowerCase())) return false;
      if (category !== 'all' && p.category !== category) return false;
      if (tagFilter.length && !tagFilter.every(t => p.tag_ids.includes(t))) return false;
      if (authorFilter.length && !authorFilter.every(a => p.author_ids.includes(a))) return false;
      return true;
    });
  }, [projects, query, category, tagFilter, authorFilter]);

  // Track previous filter signature to detect changes and trigger animation
  const filterSig = `${query}|${category}|${tagFilter.join(',')}|${authorFilter.join(',')}`;
  const prevFilterSig = React.useRef(filterSig);

  React.useEffect(() => {
    if (prevFilterSig.current !== filterSig && gridRef.current) {
      // filters changed — fade out, wait, then fade in with fresh animation
      setGridFade(true);
      const t = setTimeout(() => {
        setAnimKey(k => k + 1);
        setGridFade(false);
      }, 220);
      prevFilterSig.current = filterSig;
      return () => clearTimeout(t);
    }
    prevFilterSig.current = filterSig;
  }, [filterSig]);

  const ready = tags && authors && projects;

  return (
    <>
      <main className="page repo" data-screen-label="02 Repositorio">
        <section className="container">
          <div className="hero" style={{ padding: '40px 0 32px' }}>
            <p className="section__label">/ repositorio · 001</p>
            <h1 className="hero__title" style={{ fontSize: 'clamp(40px, 5vw, 72px)' }}>
              Todo, <em>buscable.</em>
            </h1>
            <p className="hero__lead" style={{ marginBottom: 0 }}>
              Filtra por nombre, categoría, tecnologías o autores. Las etiquetas y autores se combinan en AND —
              si seleccionas dos, solo verás proyectos que tengan ambos.
            </p>
          </div>

          {/* Search + filters */}
          <div className="search-wrap">
            <div className="search-input">
              <span className="search-icon"><Icon.Search /></span>
              <input
                type="text"
                placeholder="Buscar por nombre…"
                value={query}
                onChange={e => setQuery(e.target.value)}
              />
            </div>
            <Dropdown
              label="Categoría"
              placeholder="Todas"
              value={category}
              onChange={setCategory}
              options={[
                { id: 'all',     name: 'Todas las categorías' },
                { id: 'project', name: 'Solo proyectos' },
                { id: 'repo',    name: 'Solo repositorios' },
              ]}
            />
          </div>

          {/* Multi-select filters */}
          {ready && (
            <div className="filter-row">
              <div className="filter-group">
                <Dropdown
                  label="Etiquetas"
                  placeholder="Todas las etiquetas"
                  multi
                  searchable
                  value={tagFilter}
                  onChange={setTagFilter}
                  options={tags.map(t => ({ id: t.id, name: t.name }))}
                />
                {tagFilter.length > 0 && (
                  <div className="filter-group__chips">
                    <span className="filter-group__count">{tagFilter.length} seleccionada{tagFilter.length === 1 ? '' : 's'}</span>
                    <div className="chip-list">
                      {tagFilter.map(id => {
                        const t = tags.find(x => x.id === id);
                        if (!t) return null;
                        return (
                          <button
                            key={id}
                            className="chip is-on"
                            onClick={() => setTagFilter(tagFilter.filter(x => x !== id))}
                          >
                            {t.name} <span className="chip__x" aria-hidden>×</span>
                          </button>
                        );
                      })}
                      <button
                        className="chip chip--ghost"
                        onClick={() => setTagFilter([])}
                      >
                        limpiar
                      </button>
                    </div>
                  </div>
                )}
              </div>
              <div className="filter-group">
                <Dropdown
                  label="Autores"
                  placeholder="Todos los autores"
                  multi
                  searchable
                  value={authorFilter}
                  onChange={setAuthorFilter}
                  options={authors.map(a => ({ id: a.id, name: a.name, sub: '@' + a.username }))}
                />
                {authorFilter.length > 0 && (
                  <div className="filter-group__chips">
                    <span className="filter-group__count">{authorFilter.length} seleccionado{authorFilter.length === 1 ? '' : 's'}</span>
                    <div className="chip-list">
                      {authorFilter.map(id => {
                        const a = authors.find(x => x.id === id);
                        if (!a) return null;
                        return (
                          <button
                            key={id}
                            className="chip is-on"
                            onClick={() => setAuthorFilter(authorFilter.filter(x => x !== id))}
                          >
                            {a.name} <span className="chip__x" aria-hidden>×</span>
                          </button>
                        );
                      })}
                      <button
                        className="chip chip--ghost"
                        onClick={() => setAuthorFilter([])}
                      >
                        limpiar
                      </button>
                    </div>
                  </div>
                )}
              </div>
            </div>
          )}

          {/* Results */}
          {ready && (
            <>
              <div className="results-meta">
                <span>{filtered.length} resultado{filtered.length === 1 ? '' : 's'}</span>
                {(tagFilter.length || authorFilter.length || query || category !== 'all') ? (
                  <button
                    className="chip"
                    onClick={() => { setTagFilter([]); setAuthorFilter([]); setQuery(''); setCategory('all'); }}
                  >
                    limpiar filtros
                  </button>
                ) : <span>orden: recientes primero</span>}
              </div>

              <div
                className={"project-grid" + (gridFade ? ' is-fading' : '')}
                ref={gridRef}
                key={animKey}
              >
                {filtered.length > 0 ? (
                  filtered.map((p, index) => (
                    <ProjectCard
                      key={p.id}
                      project={p}
                      authors={authors}
                      tags={tags}
                      onOpen={setOpen}
                      style={{ '--i': Math.min(index, 12) }}
                    />
                  ))
                ) : (
                  <div className="empty" style={{ gridColumn: '1 / -1' }}>No hay proyectos con esos filtros. Intenta quitar alguno.</div>
                )}
              </div>
            </>
          )}
        </section>
      </main>

      {open && (
        <ProjectModal
          project={open}
          authors={authors}
          tags={tags}
          onClose={() => setOpen(null)}
        />
      )}
    </>
  );
}

window.RepoApp = RepoApp;
