/* global React, Icon, Avatar */

// Shared customizable "page block" system: section blocks (markdown + links) and
// per-person checklists, with read/edit renderers, half/full layout, drag-reorder, and
// a page accent palette. Used by both the Project Hub and the Dashboard's customizable
// tiles. Project-specific pieces (team panel, Drive browser) stay in module-project-hub.jsx.
// See tasks/project-resource-hub.md and tasks/dashboard-customizable-tiles.md.

const { useState: usePbState } = React;

// Page-level accent palette. Each accent sets three CSS vars on the page root so the
// cover, block titles, and link chips share one coherent color.
const ACCENTS = {
  coral:  { label: 'Coral',  solid: '#e0735a', soft: '#fbeae3', deep: '#c25a47' },
  teal:   { label: 'Teal',   solid: '#2f9e8f', soft: '#e3f3f0', deep: '#247c70' },
  violet: { label: 'Violet', solid: '#6f5bd6', soft: '#ece9fb', deep: '#574bb0' },
  amber:  { label: 'Amber',  solid: '#d99524', soft: '#fbf0db', deep: '#b3781a' },
  blue:   { label: 'Blue',   solid: '#3a72b8', soft: '#e6effb', deep: '#2f5e99' },
  steel:  { label: 'Steel',  solid: '#3d6075', soft: '#e4ebef', deep: '#355366' },
};
const accentVars = (accent) => {
  const a = ACCENTS[accent] || ACCENTS.coral;
  return { '--pa': a.solid, '--pa-soft': a.soft, '--pa-deep': a.deep };
};

// Curated link categories — `type` only drives the card icon; label/URL are free, and
// the generic 'link' type covers anything new.
const LINK_TYPES = [
  { value: 'zoom', label: 'Zoom', icon: 'play' },
  { value: 'linear', label: 'Linear', icon: 'check-square' },
  { value: 'drive', label: 'Google Drive', icon: 'folder' },
  { value: 'github', label: 'GitHub', icon: 'github' },
  { value: 'docs', label: 'Docs', icon: 'doc' },
  { value: 'link', label: 'Link', icon: 'external' },
];
const linkMeta = (type) =>
  LINK_TYPES.find((t) => t.value === type) || { value: type, label: type || 'Link', icon: 'external' };

const newId = (prefix) => `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;

function hostOf(url) {
  try { return new URL(url).hostname.replace(/^www\./, ''); } catch (e) { return url; }
}

function initialsOf(name) {
  const parts = String(name || '').trim().split(/\s+/).filter(Boolean);
  if (parts.length === 0) return '?';
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}

// --- Minimal, escape-first markdown for the rich-text block ------------------
// Content is escaped before any transform, and links are restricted to http(s), so the
// rendered HTML is safe to inject for internal authenticated authors.
function escapeHtml(s) {
  return String(s).replace(/[&<>"']/g, (c) =>
    ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
}
function mdInline(t) {
  // Build anchors as placeholders ( N ) so later passes — including the
  // bare-URL autolinker — never re-process a URL that's already inside an <a>.
  const stash = [];
  const hold = (html) => ` ${stash.push(html) - 1} `;
  const anchor = (url, label) => hold(`<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>`);

  let s = t
    .replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, (_, label, url) => anchor(url, label))
    .replace(/(https?:\/\/[^\s< ]+?)([.,;:!?)\]}'"]*)(?=[\s< ]|$)/g,
      (_, url, trail) => anchor(url, url) + trail);

  s = s
    .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
    .replace(/(^|[^*])\*([^*]+)\*/g, '$1<em>$2</em>')
    .replace(/`([^`]+)`/g, '<code>$1</code>');

  return s.replace(/ (\d+) /g, (_, i) => stash[Number(i)]);
}
function renderMarkdown(src) {
  const lines = escapeHtml(src || '').split('\n');
  const out = [];
  let list = null;
  const closeList = () => { if (list) { out.push(`</${list}>`); list = null; } };
  for (const raw of lines) {
    const line = raw.trim();
    if (!line) { closeList(); continue; }
    let m;
    if ((m = line.match(/^(#{1,3})\s+(.*)$/))) {
      const lvl = m[1].length + 1; // # -> h2, ## -> h3, ### -> h4
      closeList(); out.push(`<h${lvl}>${mdInline(m[2])}</h${lvl}>`); continue;
    }
    if ((m = line.match(/^[-*]\s+\[([ xX])\]\s+(.*)$/))) {
      if (list !== 'ul') { closeList(); out.push('<ul class="md-tasks">'); list = 'ul'; }
      const done = m[1].toLowerCase() === 'x';
      out.push(`<li class="md-task${done ? ' done' : ''}">${done ? '☑' : '☐'} ${mdInline(m[2])}</li>`); continue;
    }
    if ((m = line.match(/^[-*]\s+(.*)$/))) {
      if (list !== 'ul') { closeList(); out.push('<ul>'); list = 'ul'; }
      out.push(`<li>${mdInline(m[1])}</li>`); continue;
    }
    if ((m = line.match(/^\d+\.\s+(.*)$/))) {
      if (list !== 'ol') { closeList(); out.push('<ol>'); list = 'ol'; }
      out.push(`<li>${mdInline(m[1])}</li>`); continue;
    }
    closeList(); out.push(`<p>${mdInline(line)}</p>`);
  }
  closeList();
  return out.join('');
}

// Group the flat block list into rows for layout: a 'full' block is its own row;
// consecutive 'half' blocks pair two-per-row (a lone trailing half fills one slot).
function groupRows(blocks) {
  const rows = [];
  for (let i = 0; i < blocks.length; i += 1) {
    const b = blocks[i];
    const next = blocks[i + 1];
    if (b.width === 'half' && next && next.width === 'half') {
      rows.push({ kind: 'half', blocks: [b, next] });
      i += 1;
    } else if (b.width === 'half') {
      rows.push({ kind: 'half', blocks: [b] });
    } else {
      rows.push({ kind: 'full', blocks: [b] });
    }
  }
  return rows;
}

// === Read-mode block rendering ==============================================
function LinkCard({ item }) {
  const meta = linkMeta(item.type);
  return (
    <a className="res-card-open" href={item.url} target="_blank" rel="noopener noreferrer">
      <span className={`res-card-icon res-type-${meta.value}`}><Icon name={meta.icon} size={18}/></span>
      <span className="res-card-body">
        <span className="res-card-label">{item.label}</span>
        <span className="res-card-host"><Icon name="external" size={11}/> {hostOf(item.url)}</span>
      </span>
    </a>
  );
}

// Per-person to-do list (read mode). Checkboxes are live unless `readOnly`: toggling
// calls onToggle, which flips `done` and autosaves the page.
function TodoWidget({ block, onToggle, readOnly = false }) {
  const groups = block.groups || [];
  return (
    <section className="pb pb-todos">
      {block.title && <h3 className="pb-title">{block.title}</h3>}
      {groups.length === 0 ? (
        <div className="pb-card muted" style={{ fontSize: 13 }}>No to-dos yet.</div>
      ) : (
        <div className="todo-groups">
          {groups.map((g) => {
            const tasks = g.tasks || [];
            const done = tasks.filter((t) => t.done).length;
            return (
              <div key={g.id} className="todo-group">
                <div className="todo-group-head">
                  <Avatar initials={initialsOf(g.name)} size={26}/>
                  <span className="todo-group-name">{g.name || 'Unassigned'}</span>
                  {tasks.length > 0 && (
                    <span className={`todo-group-count${done === tasks.length ? ' is-done' : ''}`}>
                      {done}/{tasks.length}
                    </span>
                  )}
                </div>
                {tasks.length === 0 ? (
                  <div className="todo-empty muted">No tasks assigned.</div>
                ) : (
                  <ul className="todo-list">
                    {tasks.map((t) => (
                      <li key={t.id} className={`todo-item${t.done ? ' done' : ''}`}>
                        <label>
                          <input type="checkbox" checked={Boolean(t.done)} disabled={readOnly}
                            onChange={() => { if (!readOnly) onToggle(block.id, g.id, t.id); }}/>
                          <span>{t.text}</span>
                        </label>
                      </li>
                    ))}
                  </ul>
                )}
              </div>
            );
          })}
        </div>
      )}
    </section>
  );
}

// Read-mode block. `readOnly` disables todo checkboxes; `chip` renders an optional
// status pill (e.g. "Everyone can edit") in the header; `onEdit` adds an inline Edit
// button (used on dashboard member-writable blocks).
function ReadBlock({ block, onToggleTask, readOnly = false, chip = null, onEdit = null }) {
  if (block.type === 'todos') {
    return (
      <div className="pb-wrap">
        {(chip || onEdit) && (
          <div className="pb-wrap-bar">
            {chip}
            {onEdit && <button type="button" className="linklike pb-inline-edit" onClick={onEdit}><Icon name="edit" size={12}/> Edit</button>}
          </div>
        )}
        <TodoWidget block={block} onToggle={onToggleTask} readOnly={readOnly}/>
      </div>
    );
  }
  const text = (block.text || '').trim();
  const items = block.items || [];
  const empty = !text && items.length === 0;
  return (
    <div className="pb-wrap">
      {(chip || onEdit) && (
        <div className="pb-wrap-bar">
          {chip}
          {onEdit && <button type="button" className="linklike pb-inline-edit" onClick={onEdit}><Icon name="edit" size={12}/> Edit</button>}
        </div>
      )}
      <section className="pb">
        {block.title && <h3 className="pb-title">{block.title}</h3>}
        {text && <div className="pb-card md" dangerouslySetInnerHTML={{ __html: renderMarkdown(block.text) }}/>}
        {items.length > 0 && (
          <div className={`pb-links${text ? ' pb-links-after' : ''}`}>
            {items.map((it) => <div key={it.id} className="res-card"><LinkCard item={it}/></div>)}
          </div>
        )}
        {empty && <div className="pb-card muted" style={{ fontSize: 13 }}>Nothing here yet.</div>}
      </section>
    </div>
  );
}

// === Edit-mode block editors ================================================
function LinkRowEdit({ item, onChange, onRemove }) {
  return (
    <div className="lr-edit">
      <select className="select" value={item.type} onChange={(e) => onChange({ type: e.target.value })}>
        {LINK_TYPES.map((t) => <option key={t.value} value={t.value}>{t.label}</option>)}
      </select>
      <input className="input" placeholder="Label" value={item.label}
        onChange={(e) => onChange({ label: e.target.value })}/>
      <input className="input" placeholder="https://…" inputMode="url" value={item.url}
        onChange={(e) => onChange({ url: e.target.value })}/>
      <button type="button" className="btn-icon" title="Remove link" onClick={onRemove}><Icon name="x" size={13}/></button>
    </div>
  );
}

// Edit-mode editor for a 'todos' block: per-person groups, each with a name and a task
// list. `team` powers a quick-pick of roster members not yet added + a name datalist.
function TodoGroupsEdit({ block, onPatch, team = [] }) {
  const groups = block.groups || [];
  const datalistId = `team-names-${block.id}`;
  const [dragTask, setDragTask] = usePbState(null);

  const patchGroup = (gid, patch) =>
    onPatch({ groups: groups.map((g) => (g.id === gid ? { ...g, ...patch } : g)) });
  const addGroup = (name = '', memberId) =>
    onPatch({ groups: [...groups, { id: newId('grp'), name, memberId, tasks: [] }] });
  const removeGroup = (gid) => onPatch({ groups: groups.filter((g) => g.id !== gid) });

  const patchTask = (gid, tid, patch) => {
    const g = groups.find((x) => x.id === gid);
    if (!g) return;
    patchGroup(gid, { tasks: (g.tasks || []).map((t) => (t.id === tid ? { ...t, ...patch } : t)) });
  };
  const addTask = (gid) => {
    const g = groups.find((x) => x.id === gid);
    patchGroup(gid, { tasks: [...((g && g.tasks) || []), { id: newId('task'), text: '', done: false }] });
  };
  const removeTask = (gid, tid) => {
    const g = groups.find((x) => x.id === gid);
    if (!g) return;
    patchGroup(gid, { tasks: (g.tasks || []).filter((t) => t.id !== tid) });
  };
  const moveTask = (gid, fromId, toId) => {
    if (!fromId || fromId === toId) return;
    const g = groups.find((x) => x.id === gid);
    if (!g) return;
    const tasks = g.tasks || [];
    const from = tasks.findIndex((t) => t.id === fromId);
    const to = tasks.findIndex((t) => t.id === toId);
    if (from < 0 || to < 0) return;
    const next = [...tasks];
    const [moved] = next.splice(from, 1);
    next.splice(to, 0, moved);
    patchGroup(gid, { tasks: next });
  };

  const takenMemberIds = new Set(groups.map((g) => g.memberId).filter(Boolean));
  const unaddedMembers = team.filter((m) => !takenMemberIds.has(m.id));

  return (
    <div className="todo-edit">
      {groups.length === 0 && (
        <div className="todo-edit-empty">
          <Icon name="users" size={15}/>
          <span>Add a person, then give them tasks.</span>
        </div>
      )}

      {groups.map((g) => {
        const tasks = g.tasks || [];
        return (
          <div key={g.id} className="todo-edit-group">
            <div className="todo-edit-group-head">
              <Avatar initials={initialsOf(g.name)} size={28}/>
              <input className="input todo-edit-name" placeholder="Person's name" value={g.name || ''}
                list={datalistId} onChange={(e) => patchGroup(g.id, { name: e.target.value })}/>
              <span className="todo-edit-tasknum">{tasks.length} task{tasks.length === 1 ? '' : 's'}</span>
              <button type="button" className="btn-icon" title="Remove person" onClick={() => removeGroup(g.id)}>
                <Icon name="trash" size={13}/>
              </button>
            </div>
            <div className="todo-edit-tasks">
              {tasks.map((t) => {
                const isDrag = dragTask && dragTask.gid === g.id && dragTask.tid === t.id;
                return (
                  <div key={t.id} className={`todo-edit-task${isDrag ? ' is-dragging' : ''}`}
                    onDragOver={(e) => { if (dragTask && dragTask.gid === g.id) { e.preventDefault(); e.stopPropagation(); } }}
                    onDrop={(e) => {
                      if (!dragTask || dragTask.gid !== g.id) return;
                      e.preventDefault();
                      e.stopPropagation();
                      moveTask(g.id, dragTask.tid, t.id);
                      setDragTask(null);
                    }}>
                    <span className="todo-task-handle" draggable title="Drag to reorder"
                      onDragStart={(e) => { e.dataTransfer.effectAllowed = 'move'; setDragTask({ gid: g.id, tid: t.id }); }}
                      onDragEnd={() => setDragTask(null)}>
                      <Icon name="grip" size={13}/>
                    </span>
                    <input type="checkbox" checked={Boolean(t.done)}
                      title="Mark done" onChange={(e) => patchTask(g.id, t.id, { done: e.target.checked })}/>
                    <input className="input" placeholder="Describe a task…" value={t.text}
                      onChange={(e) => patchTask(g.id, t.id, { text: e.target.value })}/>
                    <button type="button" className="btn-icon" title="Remove task" onClick={() => removeTask(g.id, t.id)}>
                      <Icon name="x" size={12}/>
                    </button>
                  </div>
                );
              })}
              <button type="button" className="todo-add-task" onClick={() => addTask(g.id)}>
                <Icon name="plus" size={13}/> Add task
              </button>
            </div>
          </div>
        );
      })}

      <datalist id={datalistId}>
        {team.map((m) => <option key={m.id} value={m.name || ''}/>)}
      </datalist>

      <div className="todo-edit-add">
        <button type="button" className="btn btn-ghost btn-sm" onClick={() => addGroup('')}>
          <Icon name="plus" size={13}/> Add a person
        </button>
        {unaddedMembers.length > 0 && (
          <div className="todo-edit-quick">
            <span className="todo-edit-quick-label">Add from team</span>
            <div className="todo-edit-quick-chips">
              {unaddedMembers.map((m) => (
                <button key={m.id} type="button" className="chip"
                  onClick={() => addGroup(m.name || '', m.id)}>
                  <Icon name="plus" size={11}/> {m.name || 'Unnamed'}
                </button>
              ))}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

// A single block editor. Optional flags keep it reusable:
//  - showWidth / showDelete (default true): the full/half toggle and delete button.
//  - showVisibility + onVisibility: the Admin/Everyone write-mode toggle (dashboard
//    shared admin editing only).
//  - dragHandlers: when omitted, the reorder rail is hidden (single-block edit).
function EditBlock({
  block, onPatch, onRemove, onWidth, dragHandlers, isDragging, isDrop, team = [],
  showWidth = true, showDelete = true, showVisibility = false, onVisibility,
}) {
  const isTodos = block.type === 'todos';
  const items = block.items || [];
  const draggable = Boolean(dragHandlers);
  const patchLink = (linkId, patch) =>
    onPatch({ items: items.map((it) => (it.id === linkId ? { ...it, ...patch } : it)) });
  const addLink = () =>
    onPatch({ items: [...items, { id: newId('lnk'), type: 'link', label: '', url: '' }] });
  const removeLink = (linkId) => onPatch({ items: items.filter((it) => it.id !== linkId) });

  return (
    <section className={`pb pb-edit${isDragging ? ' is-dragging' : ''}${isDrop ? ' is-drop' : ''}`}
      onDragOver={(e) => { if (draggable && dragHandlers.active) e.preventDefault(); }}
      onDrop={(e) => { if (!draggable) return; e.preventDefault(); dragHandlers.onDrop(block.id); }}>
      {draggable && (
        <div className="pb-edit-rail" draggable title="Drag to reorder"
          onDragStart={() => dragHandlers.onStart(block.id)} onDragEnd={dragHandlers.onEnd}>
          <Icon name="grip" size={15}/>
        </div>
      )}

      <div className="pb-edit-content">
        <div className="pb-edit-head">
          <span className={`pb-edit-kind ${isTodos ? 'is-todos' : 'is-section'}`}>
            <Icon name={isTodos ? 'check-square' : 'layers'} size={12}/> {isTodos ? 'Checklist' : 'Section'}
          </span>
          <div className="pb-edit-tools">
            {showVisibility && (
              <div className="pb-width-seg pb-vis-seg" role="group" aria-label="Who can edit">
                <button type="button" className={block.writeMode !== 'members' ? 'active' : ''}
                  title="Only admins can edit; everyone can read" onClick={() => onVisibility('admin')}>
                  Admin only
                </button>
                <button type="button" className={block.writeMode === 'members' ? 'active' : ''}
                  title="Everyone can edit this section" onClick={() => onVisibility('members')}>
                  <Icon name="users" size={11}/> Everyone
                </button>
              </div>
            )}
            {showWidth && (
              <div className="pb-width-seg" role="group" aria-label="Block width">
                <button type="button" className={block.width !== 'half' ? 'active' : ''}
                  title="Full width" onClick={() => onWidth('full')}>Full</button>
                <button type="button" className={block.width === 'half' ? 'active' : ''}
                  title="Half width — pairs beside another half block" onClick={() => onWidth('half')}>Half</button>
              </div>
            )}
            {showDelete && (
              <button type="button" className="btn-icon pb-edit-del" title="Delete block" onClick={onRemove}>
                <Icon name="trash" size={14}/>
              </button>
            )}
          </div>
        </div>

        <div className="pb-title-edit">
          <input className="input pb-title-input"
            placeholder={isTodos ? 'Checklist title (optional)' : 'Section title (optional)'} value={block.title}
            onChange={(e) => onPatch({ title: e.target.value })}/>
        </div>

        {isTodos ? (
          <TodoGroupsEdit block={block} onPatch={onPatch} team={team}/>
        ) : (
          <>
            <textarea className="input pb-text-input" rows={5} value={block.text || ''}
              placeholder="Write anything — goals, agenda, notes… (optional)"
              onChange={(e) => onPatch({ text: e.target.value })}
              style={{ resize: 'vertical', fontFamily: 'inherit', lineHeight: 1.55 }}/>
            <div className="pb-md-hint">
              Markdown: <span className="mono"># heading</span> · <span className="mono">**bold**</span> ·
              <span className="mono"> - list</span> · <span className="mono"> - [ ] task</span> ·
              <span className="mono"> [link](https://…)</span>
            </div>

            <div className="pb-links-edit">
              <span className="pb-links-edit-label">Links</span>
              {items.map((it) => (
                <LinkRowEdit key={it.id} item={it} onChange={(p) => patchLink(it.id, p)} onRemove={() => removeLink(it.id)}/>
              ))}
              <button type="button" className="linklike pb-add-link" onClick={addLink}><Icon name="plus" size={12}/> Add link</button>
            </div>
          </>
        )}
      </div>
    </section>
  );
}

// A slim insert affordance between rows (edit mode). Hovering reveals a "+"; clicking
// opens a compact type picker that inserts a new block at this exact position.
function InsertDivider({ index, onInsert }) {
  const [open, setOpen] = usePbState(false);
  if (open) {
    return (
      <div className="pb-insert is-open">
        <div className="pb-insert-menu">
          <span className="pb-insert-menu-label">Insert here</span>
          <button type="button" className="btn btn-ghost btn-sm"
            onClick={() => { onInsert('section', index); setOpen(false); }}>
            <Icon name="layers" size={13}/> Section
          </button>
          <button type="button" className="btn btn-ghost btn-sm"
            onClick={() => { onInsert('todos', index); setOpen(false); }}>
            <Icon name="check-square" size={13}/> Checklist
          </button>
          <button type="button" className="btn-icon" title="Cancel" onClick={() => setOpen(false)}>
            <Icon name="x" size={13}/>
          </button>
        </div>
      </div>
    );
  }
  return (
    <div className="pb-insert">
      <button type="button" className="pb-insert-line" title="Insert a block here" onClick={() => setOpen(true)}>
        <span className="pb-insert-plus"><Icon name="plus" size={13}/></span>
      </button>
    </div>
  );
}

// Build a fresh block of the given type (shared by hub + dashboard add/insert flows).
function makeBlock(type = 'section') {
  const base = { id: newId('blk'), width: 'full', title: '' };
  return type === 'todos'
    ? { ...base, type: 'todos', groups: [] }
    : { ...base, type: 'section', text: '', items: [] };
}

Object.assign(window, {
  ACCENTS, accentVars, LINK_TYPES, linkMeta, newId, hostOf, initialsOf,
  renderMarkdown, groupRows, makeBlock,
  LinkCard, TodoWidget, ReadBlock, LinkRowEdit, TodoGroupsEdit, EditBlock, InsertDivider,
});
