// Projects landing page — list view with favourites, archive, and 30-day trash.
// Uses design-system tokens from /styles.css (table-wrap, table, tbl-row, btn, seg).
//
// Storage helpers (loadProjectsIndex, saveProjectsIndex, createProject,
// updateProjectMeta, trashProject, restoreProject, deleteProjectStorage,
// purgeExpiredDeleted, projectKey) are defined in storage.jsx and live on window.

const { useState: useStateP, useEffect: useEffectP } = React;

function fmtRelative(ts) {
  if (!ts) return '';
  const diff = Date.now() - ts;
  const sec = Math.floor(diff / 1000);
  if (sec < 60) return 'just now';
  const min = Math.floor(sec / 60);
  if (min < 60) return `${min}m ago`;
  const hr = Math.floor(min / 60);
  if (hr < 24) return `${hr}h ago`;
  const day = Math.floor(hr / 24);
  if (day < 7) return `${day}d ago`;
  if (day < 30) return `${Math.floor(day / 7)}w ago`;
  return new Date(ts).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
}
function fmtTrashCountdown(deletedAt) {
  const remaining = window.TRASH_TTL_MS - (Date.now() - deletedAt);
  if (remaining <= 0) return { label: 'expiring…', level: 'danger' };
  const days = Math.ceil(remaining / (24 * 60 * 60 * 1000));
  let level = 'ok';
  if (days <= 3) level = 'danger';
  else if (days <= 7) level = 'urgent';
  return { label: `in ${days} day${days === 1 ? '' : 's'}`, level };
}

function ProjectsPage() {
  const [theme, setTheme] = useStateP(() => localStorage.getItem('pp-theme') || 'dark');
  const [filter, setFilter] = useStateP('all');     // 'all' | 'favourites' | 'archived' | 'deleted'
  const [all, setAll] = useStateP(() => window.loadProjectsIndex() || []);
  const [renamingId, setRenamingId] = useStateP(null);
  // Entitlement (defaults to Solo if billing.jsx hasn't loaded, so we never
  // wrongly block when the plan is simply unknown).
  const ent = (typeof window.useEntitlement === 'function') ? window.useEntitlement() : { solo: true, plan: 'solo' };
  const FREE_STORYBOARD_LIMIT = 5;

  // Apply theme to <html>
  useEffectP(() => {
    document.documentElement.dataset.theme = theme;
    localStorage.setItem('pp-theme', theme);
  }, [theme]);

  // Refresh index from storage (called after every mutation)
  const refresh = () => setAll(window.loadProjectsIndex() || []);

  // The background cloud reconcile (root.jsx) fires this once it has merged
  // server rows into localStorage — re-read so freshly-pulled projects show
  // up without a manual refresh.
  useEffectP(() => {
    const onUpdated = () => setAll(window.loadProjectsIndex() || []);
    window.addEventListener('pp-projects-updated', onUpdated);
    return () => window.removeEventListener('pp-projects-updated', onUpdated);
  }, []);

  // ──────────────── Derived data ────────────────
  const active = all.filter(p => !p.deletedAt);
  const totalShots = active.reduce((n, p) => n + (p.shotCount || 0), 0);
  const counts = {
    all: active.filter(p => !p.archived).length,
    favourites: active.filter(p => p.favourite && !p.archived).length,
    archived: active.filter(p => p.archived).length,
    deleted: all.filter(p => p.deletedAt).length,
  };
  // Free plan: only non-deleted storyboards count toward the limit (binned ones
  // don't, and can't be opened — delete to free a slot).
  const overStoryboardLimit = !ent.solo && active.length >= FREE_STORYBOARD_LIMIT;

  let list;
  if (filter === 'favourites')  list = active.filter(p => p.favourite && !p.archived);
  else if (filter === 'archived') list = active.filter(p => p.archived);
  else if (filter === 'deleted')  list = all.filter(p => p.deletedAt);
  else                            list = active.filter(p => !p.archived);

  list = [...list].sort((a, b) => {
    if (filter === 'all' && a.favourite !== b.favourite) return a.favourite ? -1 : 1;
    if (filter === 'deleted') return (b.deletedAt || 0) - (a.deletedAt || 0);
    return (b.updatedAt || 0) - (a.updatedAt || 0);
  });

  // ──────────────── Actions ────────────────
  const onNew = () => {
    if (overStoryboardLimit) {
      if (window.ppOpenUpgrade) window.ppOpenUpgrade(`You've reached the Free limit of ${FREE_STORYBOARD_LIMIT} storyboards. Upgrade to Solo for unlimited — or delete one to free a slot.`);
      return;
    }
    const id = window.createProject('Untitled Shot List');
    // `new=1` tells the editor this is a fresh project so it can pop the
    // Project Details modal immediately and prompt for metadata.
    location.href = `${location.pathname}?id=${encodeURIComponent(id)}&new=1`;
  };
  const onOpen = (p) => {
    if (p.deletedAt) return;
    location.href = `${location.pathname}?id=${encodeURIComponent(p.id)}`;
  };
  const onToggleFav = (p) => {
    const idx = window.loadProjectsIndex() || [];
    const e = idx.find(x => x.id === p.id);
    if (e) { e.favourite = !e.favourite; window.saveProjectsIndex(idx); refresh(); }
  };
  const onToggleArchive = (p) => {
    const idx = window.loadProjectsIndex() || [];
    const e = idx.find(x => x.id === p.id);
    if (e) { e.archived = !e.archived; window.saveProjectsIndex(idx); refresh(); }
  };
  const onTrash = (p) => { window.trashProject(p.id); refresh(); };
  const onRestore = (p) => { window.restoreProject(p.id); refresh(); };
  const onPurge = (p) => {
    if (!confirm(`Delete "${p.title || 'Untitled'}" forever? This cannot be undone.`)) return;
    window.deleteProjectStorage(p.id);
    refresh();
  };
  const onCommitRename = (p, newTitle) => {
    const title = (newTitle || '').trim() || 'Untitled';
    window.updateProjectMeta(p.id, { title });
    // Also patch the per-project state if it exists, so the editor matches.
    try {
      const raw = localStorage.getItem(window.projectKey(p.id));
      if (raw) {
        const data = JSON.parse(raw);
        if (data.project) data.project.title = title;
        else data.title = title;
        localStorage.setItem(window.projectKey(p.id), JSON.stringify(data));
      }
    } catch (e) {}
    setRenamingId(null);
    refresh();
  };

  // ──────────────── Render ────────────────
  return (
    <div className="app" data-screen-label="Projects">
      <Rail page="shots" />
      <main className="main">
        <Header theme={theme} setTheme={setTheme} crumbs={['Shot List', 'Projects']} />

        <div className="subheader">
          <div className="subheader-left">
            <div className="subheader-eyebrow"><span className="dot" /> Library</div>
            <h1>Projects</h1>
            {all.length > 0 && (
              <div className="subheader-meta">
                <span><strong>{active.length}</strong> project{active.length === 1 ? '' : 's'}</span>
                <span className="sep">·</span>
                <span><strong>{totalShots}</strong> shot{totalShots === 1 ? '' : 's'} total</span>
              </div>
            )}
          </div>
          <div className="subheader-right">
            {!ent.solo && (
              <span className={`plan-limit-chip ${overStoryboardLimit ? 'at-limit' : ''}`} title="Free plan storyboard limit">
                {active.length} / {FREE_STORYBOARD_LIMIT}
              </span>
            )}
            <button className="btn btn-primary" onClick={onNew}><IconPlus /> New project</button>
          </div>
        </div>

        {overStoryboardLimit && (
          <div className="plan-limit-banner">
            <span>You're at the Free limit of {FREE_STORYBOARD_LIMIT} storyboards. Upgrade to Solo for unlimited — or delete one to free a slot.</span>
            <button className="btn btn-primary" onClick={() => window.ppOpenUpgrade && window.ppOpenUpgrade()}>Upgrade</button>
          </div>
        )}

        <div className="toolbar">
          <div className="seg" role="group" aria-label="Filter">
            <button className={filter==='all' ? 'active' : ''} onClick={() => setFilter('all')}>
              All <span className="filter-count">{counts.all}</span>
            </button>
            <button className={filter==='favourites' ? 'active' : ''} onClick={() => setFilter('favourites')}>
              <IconStar /> Favourites <span className="filter-count">{counts.favourites}</span>
            </button>
            <button className={filter==='archived' ? 'active' : ''} onClick={() => setFilter('archived')}>
              <IconArchive /> Archived <span className="filter-count">{counts.archived}</span>
            </button>
            <button className={filter==='deleted' ? 'active' : ''} onClick={() => setFilter('deleted')}>
              <IconTrash /> Deleted <span className="filter-count">{counts.deleted}</span>
            </button>
          </div>
          <div className="toolbar-spacer" />
        </div>

        <div className="scroll">
          {all.length === 0 ? (
            <div className="empty-state" style={{ paddingTop: 100 }}>
              <h2 style={{ fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 600, color: 'var(--text)', margin: '0 0 8px' }}>No projects yet</h2>
              <p style={{ fontSize: 14, color: 'var(--text-3)', marginBottom: 24 }}>Create your first shot list to get started.</p>
              <button className="btn btn-primary" onClick={onNew}><IconPlus /> New project</button>
            </div>
          ) : list.length === 0 ? (
            <div className="empty-state">No projects match this filter.</div>
          ) : (
            <div className="table-wrap" style={{ marginTop: 4 }}>
              <table className="table">
                <thead>
                  <tr>
                    <th style={{ width: 48 }}></th>
                    <th>Name</th>
                    <th style={{ width: 180 }}>{filter === 'deleted' ? 'Expires' : 'Modified'}</th>
                    <th style={{ width: 90 }}>Shots</th>
                    <th style={{ width: 130 }}></th>
                  </tr>
                </thead>
                <tbody>
                  {list.map(p => (
                    <ProjectRow
                      key={p.id}
                      project={p}
                      isTrashed={!!p.deletedAt}
                      renaming={renamingId === p.id}
                      onOpen={onOpen}
                      onToggleFav={onToggleFav}
                      onToggleArchive={onToggleArchive}
                      onTrash={onTrash}
                      onRestore={onRestore}
                      onPurge={onPurge}
                      onStartRename={() => setRenamingId(p.id)}
                      onCommitRename={onCommitRename}
                      onCancelRename={() => setRenamingId(null)}
                    />
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      </main>
    </div>
  );
}

function ProjectRow({ project: p, isTrashed, renaming, onOpen, onToggleFav, onToggleArchive, onTrash, onRestore, onPurge, onStartRename, onCommitRename, onCancelRename }) {
  const cls = [
    'tbl-row',
    isTrashed ? 'is-trashed' : '',
    p.archived ? 'is-archived' : '',
  ].filter(Boolean).join(' ');

  const onRowClick = (e) => {
    if (renaming) return;
    if (e.target.closest('button, input')) return;
    onOpen(p);
  };

  return (
    <tr className={cls} onClick={onRowClick} style={{ cursor: isTrashed ? 'default' : 'pointer', opacity: isTrashed ? 0.7 : p.archived ? 0.65 : 1 }}>
      <td>
        {isTrashed ? (
          <span title="In trash" style={{ display: 'grid', placeItems: 'center', color: 'var(--text-4)', opacity: 0.5 }}>
            <IconTrash />
          </span>
        ) : (
          <button
            className="icon-btn"
            style={{
              background: 'transparent',
              border: 'none',
              boxShadow: 'none',
              color: p.favourite ? 'var(--status-review)' : 'var(--text-4)',
            }}
            title={p.favourite ? 'Unfavourite' : 'Favourite'}
            onClick={(e) => { e.stopPropagation(); onToggleFav(p); }}
          >
            <svg viewBox="0 0 24 24" fill={p.favourite ? 'currentColor' : 'none'} stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" width="16" height="16">
              <path d="M12 3l2.6 5.6 6.1.7-4.5 4.2 1.2 6.1L12 16.8l-5.5 2.8 1.2-6.1L3.3 9.3l6-.7z"/>
            </svg>
          </button>
        )}
      </td>
      <td>
        {renaming ? (
          <input
            className="inline-input title"
            autoFocus
            defaultValue={p.title || ''}
            onKeyDown={(e) => {
              if (e.key === 'Enter') { e.preventDefault(); onCommitRename(p, e.target.value); }
              if (e.key === 'Escape') { e.preventDefault(); onCancelRename(); }
            }}
            onBlur={(e) => onCommitRename(p, e.target.value)}
            onClick={(e) => e.stopPropagation()}
          />
        ) : (
          <span style={{ color: 'var(--text)', fontWeight: 500, fontSize: 13.5 }}>{p.title || 'Untitled'}</span>
        )}
      </td>
      <td style={{ color: 'var(--text-3)', fontSize: 12.5, fontFamily: 'var(--font-mono)' }}>
        {isTrashed
          ? (() => {
              const cd = fmtTrashCountdown(p.deletedAt);
              const color = cd.level === 'danger' ? '#f87171' : cd.level === 'urgent' ? 'var(--status-review)' : 'var(--text-3)';
              return <span style={{ color }}>Auto-deletes {cd.label}</span>;
            })()
          : fmtRelative(p.updatedAt || p.createdAt)}
      </td>
      <td style={{ color: 'var(--text-3)', fontVariantNumeric: 'tabular-nums', fontFamily: 'var(--font-mono)', fontSize: 12.5 }}>
        {p.shotCount || 0}
      </td>
      <td onClick={(e) => e.stopPropagation()}>
        <div className="col-actions-inner" style={{ justifyContent: 'flex-end' }}>
          {isTrashed ? (
            <>
              <button className="icon-btn" title="Restore" onClick={() => onRestore(p)}>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" width="16" height="16">
                  <path d="M3 12a9 9 0 1 0 3-6.7"/>
                  <path d="M3 4v5h5"/>
                </svg>
              </button>
              <button className="icon-btn" title="Delete forever" onClick={() => onPurge(p)}>
                <IconTrash />
              </button>
            </>
          ) : (
            <>
              <button className="icon-btn" title="Rename" onClick={onStartRename}><IconEdit /></button>
              <button className="icon-btn" title={p.archived ? 'Unarchive' : 'Archive'} onClick={() => onToggleArchive(p)}>
                <IconArchive />
              </button>
              <button className="icon-btn" title="Move to trash" onClick={() => onTrash(p)}>
                <IconTrash />
              </button>
            </>
          )}
        </div>
      </td>
    </tr>
  );
}

// IconArchive is page-specific — not defined in icons.jsx
const IconArchive = (props) => (
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...props}>
    <rect x="3" y="4" width="18" height="4" rx="1"/>
    <path d="M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8"/>
    <path d="M10 12h4"/>
  </svg>
);

window.ProjectsPage = ProjectsPage;
