﻿import { useState, useMemo, useEffect, useCallback } from 'react';
import { GitCommitDrawer } from '../components/GitCommitDrawer';
import {
  Button, Callout, Divider, Drawer, FormGroup,
  HTMLSelect, HTMLTable, InputGroup, Intent,
  NonIdealState, Spinner, Tab, Tabs, Tag, TextArea, Tooltip,
  EditableText,
} from '@blueprintjs/core';
import type { Opc, OpcArtifact, OpcNote, OpcPriority, OpcStatus, OpcType, ArtifactType } from '../types/opc';
import {
  listOpcs, createOpc, updateOpc, getNextNumber,
  listNotes, addNote,
  listArtifacts, createArtifact, updateArtifact, deleteArtifact,
  getLinkedCommits, getPinnedCommits, pinCommit, unpinCommit, getBranchCoverageForRepo,
  listGiteaBranches, createGiteaBranch,
  aiAssist,
  type LinkedCommit, type PinnedCommit, type BranchCoverage, type GiteaBranch,
} from '../api/opcApi';

// -- Label / intent maps -------------------------------------------------------

const TYPE_LABELS: Record<OpcType, string> = {
  ChangeOrder:         'Change Order',
  NonDevTask:          'Non-Dev Task',
  QaTask:              'QA Task',
  BusinessRequirement: 'Business Req.',
  Feature:             'Feature',
  General:             'General',
};

const TYPE_INTENT: Record<OpcType, Intent> = {
  ChangeOrder:         Intent.PRIMARY,
  NonDevTask:          Intent.NONE,
  QaTask:              Intent.WARNING,
  BusinessRequirement: Intent.SUCCESS,
  Feature:             Intent.SUCCESS,
  General:             Intent.NONE,
};

const STATUS_INTENT: Record<OpcStatus, Intent> = {
  New:        Intent.PRIMARY,
  InProgress: Intent.WARNING,
  InReview:   Intent.PRIMARY,
  Blocked:    Intent.DANGER,
  Closed:     Intent.SUCCESS,
  Cancelled:  Intent.NONE,
};

const STATUS_LABELS: Record<OpcStatus, string> = {
  New:        'New',
  InProgress: 'In Progress',
  InReview:   'In Review',
  Blocked:    'Blocked',
  Closed:     'Closed',
  Cancelled:  'Cancelled',
};

const PRIORITY_INTENT: Record<OpcPriority, Intent> = {
  Low:      Intent.NONE,
  Medium:   Intent.WARNING,
  High:     Intent.DANGER,
  Critical: Intent.DANGER,
};

const ARTIFACT_TABS: { type: ArtifactType; label: string; placeholder: string }[] = [
  { type: 'BusinessRequirement', label: 'Business Req.', placeholder: 'Document business requirements, JAD/JAR outputs, acceptance criteria...' },
  { type: 'Rule',                label: 'Rules',         placeholder: 'Define rule engine rules, validation logic, business rules...' },
  { type: 'Spec',                label: 'Spec',          placeholder: 'Technical specification — data contracts, API shapes, architecture decisions...' },
  { type: 'Documentation',       label: 'Docs',          placeholder: 'End-user or developer documentation for this change...' },
  { type: 'QaTestPath',          label: 'QA Test Paths', placeholder: 'Step-by-step QA test scenarios, edge cases, regression checks...' },
];

// -- SDLC delivery chain -------------------------------------------------------

const SDLC_STAGES: { branch: string; label: string; intent: Intent }[] = [
  { branch: 'develop', label: 'Dev',        intent: Intent.PRIMARY  },
  { branch: 'staging', label: 'Staging',    intent: Intent.WARNING  },
  { branch: 'uat',     label: 'UAT',        intent: Intent.DANGER   },
  { branch: 'main',    label: 'Production', intent: Intent.SUCCESS  },
];

function deriveSdlcSummary(coverage: BranchCoverage[]): { label: string; intent: Intent } | null {
  for (let i = SDLC_STAGES.length - 1; i >= 0; i--) {
    const stage = SDLC_STAGES[i];
    const hit = coverage.find(c => c.branch === stage.branch);
    if (hit?.contains) return { label: stage.label, intent: stage.intent };
  }
  return null;
}

// Aggregate per-repo branch coverage into a single view.
// A stage is "reached" only when every repo that recognised at least one hash
// reports contains=true for that branch. Repos that recognised no hashes are
// excluded from the constraint (they have no code linked to this OPC).
const KNOWN_REPOS = ['Clarity', 'OPC', 'Gateway'] as const;

type RepoCoverage = { repoKey: string; coverage: BranchCoverage[] };

function aggregateCoverage(perRepo: RepoCoverage[]): BranchCoverage[] {
  const active = perRepo.filter(r => r.coverage.length > 0);
  if (active.length === 0) return [];
  const branches = [...new Set(active.flatMap(r => r.coverage.map(c => c.branch)))];
  return branches.map(branch => {
    const entries = active
      .map(r => r.coverage.find(c => c.branch === branch))
      .filter((c): c is BranchCoverage => c !== undefined);
    return {
      branch,
      contains: entries.length > 0 && entries.every(c => c.contains),
      tipHash:  entries[0]?.tipHash ?? '',
      isHead:   entries.some(c => c.isHead),
    };
  });
}

// -- Helpers -------------------------------------------------------------------

function fmtDate(iso: string): string {
  return new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}

function fmtDateTime(iso: string): string {
  return new Date(iso).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
}

// -- AI Assist Box -------------------------------------------------------------

function AiAssistBox({ context, onApply }: { context?: string; onApply: (text: string) => void }) {
  const [prompt,  setPrompt]  = useState('');
  const [result,  setResult]  = useState('');
  const [loading, setLoading] = useState(false);
  const [error,   setError]   = useState<string | null>(null);

  const run = async () => {
    if (!prompt.trim()) return;
    setLoading(true); setError(null); setResult('');
    try {
      const text = await aiAssist(prompt.trim(), context);
      setResult(text);
    } catch (e) { setError(String(e)); }
    finally { setLoading(false); }
  };

  return (
    <div className="opc-ai-box">
      <div className="opc-ai-label">
        <span className="opc-field-label" style={{ color: '#7c3aed' }}>AI Assist</span>
      </div>
      <div className="opc-ai-input-row">
        <InputGroup
          placeholder="Ask AI to draft content (e.g. 'Write acceptance criteria for...')"
          value={prompt}
          onChange={e => setPrompt(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); run(); } }}
          rightElement={
            <Tooltip content="Send prompt" placement="top">
              <Button icon="arrow-up" minimal intent={Intent.PRIMARY} loading={loading}
                onClick={run} disabled={!prompt.trim() || loading} />
            </Tooltip>
          }
        />
      </div>
      {error && <Callout intent={Intent.DANGER} icon="error" style={{ marginTop: '0.5rem', fontSize: '0.8rem' }}>{error}</Callout>}
      {result && (
        <div className="opc-ai-result">
          <pre className="opc-ai-result-text">{result}</pre>
          <div className="opc-ai-result-actions">
            <Button icon="arrow-up" intent={Intent.PRIMARY} text="Copy to field above" small
              onClick={() => { onApply(result); setResult(''); setPrompt(''); }} />
            <Button icon="cross" text="Dismiss" small minimal onClick={() => setResult('')} />
          </div>
        </div>
      )}
    </div>
  );
}

// -- Artifact panel ------------------------------------------------------------

function ArtifactPanel({ opcId, opcNumber, artifactType, placeholder }: {
  opcId: string; opcNumber: string; artifactType: ArtifactType; placeholder: string;
}) {
  const [artifacts, setArtifacts] = useState<OpcArtifact[]>([]);
  const [loading,   setLoading]   = useState(true);
  const [saving,    setSaving]    = useState(false);
  const [editId,    setEditId]    = useState<string | null>(null);
  const [editTitle, setEditTitle] = useState('');
  const [editBody,  setEditBody]  = useState('');
  const [adding,    setAdding]    = useState(false);

  const load = useCallback(async () => {
    setLoading(true);
    try { setArtifacts(await listArtifacts(opcId, artifactType)); }
    catch { /* API may not be up */ }
    finally { setLoading(false); }
  }, [opcId, artifactType]);

  useEffect(() => { load(); }, [load]);

  const startAdd  = () => { setEditId(null); setEditTitle(''); setEditBody(''); setAdding(true); };
  const startEdit = (a: OpcArtifact) => { setAdding(false); setEditId(a.id); setEditTitle(a.title); setEditBody(a.content); };
  const cancel    = () => { setAdding(false); setEditId(null); };

  const save = async () => {
    setSaving(true);
    try {
      if (editId) {
        const u = await updateArtifact(editId, { artifactType, title: editTitle, content: editBody });
        setArtifacts(prev => prev.map(a => a.id === editId ? u : a));
      } else {
        const c = await createArtifact(opcId, { artifactType, title: editTitle, content: editBody });
        setArtifacts(prev => [...prev, c]);
      }
      cancel();
    } catch { /* no-op */ }
    finally { setSaving(false); }
  };

  const remove = async (id: string) => {
    await deleteArtifact(id);
    setArtifacts(prev => prev.filter(a => a.id !== id));
  };

  const isEditing = adding || editId !== null;

  if (loading) return <div style={{ padding: '2rem', textAlign: 'center' }}><Spinner size={20} /></div>;

  return (
    <div className="opc-artifact-panel">
      {!isEditing && (
        <>
          {artifacts.length === 0 ? (
            <NonIdealState icon="document" title="Nothing here yet"
              description={`Add the first entry for ${opcNumber}.`}
              action={<Button icon="plus" intent={Intent.PRIMARY} text="Add Entry" onClick={startAdd} />} />
          ) : (
            <>
              {artifacts.map(a => (
                <div key={a.id} className="opc-artifact-card">
                  <div className="opc-artifact-card-header">
                    <strong>{a.title || '(untitled)'}</strong>
                    <div style={{ display: 'flex', gap: '0.3rem' }}>
                      <Button icon="edit" minimal small onClick={() => startEdit(a)} />
                      <Button icon="trash" minimal small intent={Intent.DANGER} onClick={() => remove(a.id)} />
                    </div>
                  </div>
                  <pre className="opc-artifact-body">{a.content}</pre>
                  <div className="opc-artifact-meta">{fmtDate(a.updatedAt)}</div>
                </div>
              ))}
              <Button icon="plus" text="Add Entry" minimal intent={Intent.PRIMARY} onClick={startAdd} style={{ marginTop: '0.5rem' }} />
            </>
          )}
        </>
      )}
      {isEditing && (
        <div className="opc-artifact-form">
          <FormGroup label="Title">
            <InputGroup placeholder="Brief title" value={editTitle}
              onChange={e => setEditTitle(e.target.value)} autoFocus />
          </FormGroup>
          <FormGroup label="Content">
            <TextArea fill rows={10} placeholder={placeholder} value={editBody}
              onChange={e => setEditBody(e.target.value)} />
          </FormGroup>
          <AiAssistBox context={editBody || undefined} onApply={t => setEditBody(prev => prev ? `${prev}\n\n${t}` : t)} />
          <div className="opc-drawer-actions" style={{ paddingTop: '0.75rem' }}>
            <Button text="Cancel" onClick={cancel} minimal />
            <Button intent={Intent.PRIMARY} text="Save" loading={saving}
              onClick={save} disabled={!editBody.trim()} />
          </div>
        </div>
      )}
    </div>
  );
}

// -- Repo badge ---------------------------------------------------------------

const REPO_INTENT: Record<string, Intent> = {
  Clarity: Intent.PRIMARY,
  OPC:     Intent.WARNING,
  Gateway: Intent.SUCCESS,
};

// -- Commit row (shared) -------------------------------------------------------

function CommitRow({ commit, onPin, isPinned, onViewDiff }: { commit: LinkedCommit; onPin?: () => void; isPinned?: boolean; onViewDiff?: (hash: string) => void }) {
  return (
    <div className="opc-commit-row">
      <Tooltip content="View diff" placement="left">
        <code className="opc-commit-hash" style={{ cursor: 'pointer' }}
          onClick={() => onViewDiff ? onViewDiff(commit.hash) : navigator.clipboard.writeText(commit.hash)}>
          {commit.shortHash}
        </code>
      </Tooltip>
      {commit.repoKey && commit.repoKey !== 'unknown' && (
        <Tag minimal round small
          intent={REPO_INTENT[commit.repoKey] ?? Intent.NONE}
          style={{ fontSize: '0.68rem', flexShrink: 0 }}>
          {commit.repoKey}
        </Tag>
      )}
      <div className="opc-commit-info">
        <div className="opc-commit-msg">{commit.subject}</div>
        <div className="opc-commit-meta">{commit.author} · {commit.date}</div>
        {commit.files.length > 0 && (
          <div className="opc-commit-files">
            {commit.files.slice(0, 5).map(f => <span key={f}>{f}</span>)}
            {commit.files.length > 5 && <span>+{commit.files.length - 5} more</span>}
          </div>
        )}
      </div>
      {onPin && (
        <Tooltip content={isPinned ? 'Locked in' : 'Lock in this commit'} placement="left">
          <Button
            icon={isPinned ? 'lock' : 'pin'}
            minimal small
            intent={isPinned ? Intent.SUCCESS : Intent.NONE}
            disabled={isPinned}
            style={{ flexShrink: 0, alignSelf: 'center' }}
            onClick={onPin}
          />
        </Tooltip>
      )}
    </div>
  );
}

// -- Commits tab ---------------------------------------------------------------

function CommitsTab({ opc, isActive }: { opc: Opc; isActive: boolean }) {
  const [autoCommits, setAutoCommits] = useState<LinkedCommit[]>([]);
  const [pinned,      setPinned]      = useState<PinnedCommit[]>([]);
  const [coverage,    setCoverage]    = useState<BranchCoverage[]>([]);
  const [loaded,      setLoaded]      = useState(false);
  const [pinInput,    setPinInput]    = useState('');
  const [pinning,     setPinning]     = useState(false);
  const [pinError,    setPinError]    = useState<string | null>(null);
  const [viewingHash, setViewingHash] = useState<string | null>(null);

  // undefined = not yet loaded, null = not found, GiteaBranch = found
  const [linkedBranch,   setLinkedBranch]   = useState<GiteaBranch | null | undefined>(undefined);
  const [creatingBranch, setCreatingBranch] = useState(false);
  const [branchError,    setBranchError]    = useState<string | null>(null);

  useEffect(() => {
    if (!isActive || loaded) return;
    (async () => {
      try {
        const [auto, pins] = await Promise.all([
          getLinkedCommits(opc.number),
          getPinnedCommits(opc.id),
        ]);
        setAutoCommits(auto);
        setPinned(pins);
        const allHashes = [...new Set([...auto.map(c => c.hash), ...pins.map(c => c.hash)])];
        if (allHashes.length > 0) {
          const perRepoCoverage = await Promise.all(
            KNOWN_REPOS.map(async repoKey => ({
              repoKey,
              coverage: await getBranchCoverageForRepo(repoKey, allHashes).catch(() => [] as BranchCoverage[]),
            }))
          );
          setCoverage(aggregateCoverage(perRepoCoverage));
        }
      } catch { /* non-critical — API may not be up */ }
      finally { setLoaded(true); }
    })();

    // Load Gitea branch independently — don't block commit rendering
    const opcTag = opc.number.replace('OPC # ', 'OPC-');
    listGiteaBranches('all')
      .then(branches => {
        const found = branches.find(b => b.name.includes(opcTag));
        setLinkedBranch(found ?? null);
      })
      .catch(() => setLinkedBranch(null));
  }, [isActive, loaded, opc.id, opc.number]);

  const handleCreateBranch = async () => {
    setCreatingBranch(true); setBranchError(null);
    try {
      const branch = await createGiteaBranch(opc.number, opc.title);
      setLinkedBranch(branch);
    } catch (e) { setBranchError(String(e)); }
    finally { setCreatingBranch(false); }
  };

  const handlePin = async () => {
    if (!pinInput.trim()) return;
    setPinning(true); setPinError(null);
    try {
      const c = await pinCommit(opc.id, pinInput.trim(), 'amadzarak');
      setPinned(prev => [...prev, c]);
      setPinInput('');
      const allHashes = [...new Set([...autoCommits.map(x => x.hash), ...pinned.map(x => x.hash), c.hash])];
      const perRepoCoverage = await Promise.all(
        KNOWN_REPOS.map(async repoKey => ({
          repoKey,
          coverage: await getBranchCoverageForRepo(repoKey, allHashes).catch(() => [] as BranchCoverage[]),
        }))
      );
      setCoverage(aggregateCoverage(perRepoCoverage));
    } catch (e) { setPinError(String(e)); }
    finally { setPinning(false); }
  };

  const handleLockIn = async (commit: LinkedCommit) => {
    try {
      const c = await pinCommit(opc.id, commit.hash, 'amadzarak');
      setPinned(prev => [...prev, c]);
      const allHashes = [...new Set([...autoCommits.map(x => x.hash), ...pinned.map(x => x.hash), c.hash])];
      const perRepoCoverage = await Promise.all(
        KNOWN_REPOS.map(async repoKey => ({
          repoKey,
          coverage: await getBranchCoverageForRepo(repoKey, allHashes).catch(() => [] as BranchCoverage[]),
        }))
      );
      setCoverage(aggregateCoverage(perRepoCoverage));
    } catch { /* no-op */ }
  };

  const handleUnpin = async (hash: string) => {
    try {
      await unpinCommit(opc.id, hash);
      const remaining = pinned.filter(c => c.hash !== hash);
      setPinned(remaining);
      const allHashes = [...new Set([...autoCommits.map(x => x.hash), ...remaining.map(x => x.hash)])];
      if (allHashes.length > 0) {
        const perRepoCoverage = await Promise.all(
          KNOWN_REPOS.map(async repoKey => ({
            repoKey,
            coverage: await getBranchCoverageForRepo(repoKey, allHashes).catch(() => [] as BranchCoverage[]),
          }))
        );
        setCoverage(aggregateCoverage(perRepoCoverage));
      } else {
        setCoverage([]);
      }
    } catch { /* no-op */ }
  };

  if (!loaded) return <NonIdealState icon={<Spinner size={20} />} title="Scanning commits..." />;

  return (
    <div className="opc-tab-panel">

      {/* Git branch */}
      <div style={{ marginBottom: '0.75rem' }}>
        <div className="opc-field-label" style={{ marginBottom: '0.4rem' }}>Git Branch</div>
        {linkedBranch === undefined ? (
          <Spinner size={14} />
        ) : linkedBranch !== null ? (
          <div style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', flexWrap: 'wrap' }}>
            <Tag icon="git-branch" minimal round intent={Intent.PRIMARY}
              style={{ fontFamily: 'monospace', fontSize: '0.8rem' }}>
              {linkedBranch.name}
            </Tag>
            <a
              href={`https://opc.clarity.test/ClarityStack/${linkedBranch.repoKey ?? 'Clarity'}/src/branch/${encodeURIComponent(linkedBranch.name)}`}
              target="_blank" rel="noreferrer"
              style={{ fontSize: '0.8rem', color: 'var(--bp4-intent-primary)' }}>
              Open in Gitea ↗
            </a>
          </div>
        ) : (
          <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
            <span className="opc-empty-hint" style={{ margin: 0 }}>No branch yet</span>
            <Button
              icon="git-branch"
              intent={Intent.PRIMARY}
              text="Create Branch"
              small
              loading={creatingBranch}
              onClick={handleCreateBranch}
            />
          </div>
        )}
        {branchError && (
          <Callout intent={Intent.DANGER} style={{ marginTop: '0.5rem', fontSize: '0.8rem' }}>
            {branchError}
          </Callout>
        )}
      </div>
      <Divider style={{ margin: '0.5rem 0 0.75rem' }} />

      {/* SDLC Delivery Chain */}
      {coverage.length > 0 && (() => {
        const summary = deriveSdlcSummary(coverage);
        return (
          <div className="opc-delivery-chain">
            <div className="opc-field-label" style={{ marginBottom: '0.6rem' }}>Delivery Chain</div>
            <div className="opc-sdlc-pipeline">
              {SDLC_STAGES.map((stage, i) => {
                const hit = coverage.find(c => c.branch === stage.branch);
                const reached = hit?.contains ?? false;
                return (
                  <div key={stage.branch} className="opc-sdlc-stage-item">
                    {i > 0 && <span className="opc-sdlc-arrow">→</span>}
                    <Tooltip content={
                      reached
                        ? `All linked commits have reached ${stage.label}`
                        : hit
                          ? `Not all linked commits have reached ${stage.label} yet`
                          : `${stage.label} branch not found locally`
                    }>
                      <Tag
                        intent={reached ? stage.intent : Intent.NONE}
                        icon={reached ? 'tick-circle' : 'circle'}
                        minimal={!reached}
                        round
                      >
                        {stage.label}
                      </Tag>
                    </Tooltip>
                  </div>
                );
              })}
            </div>
            {summary && (
              <div className="opc-sdlc-furthest">
                Furthest: <strong>{summary.label}</strong>
              </div>
            )}
          </div>
        );
      })()}

      {/* Auto-detected */}
      <div className="opc-commits-section-label">
        <span className="opc-field-label">Auto-linked</span>
        <span className="opc-section-hint"> — commits with <code>{opc.number}</code> in the message</span>
      </div>
      {autoCommits.length === 0
        ? <p className="opc-empty-hint">None found — include <strong>{opc.number}</strong> in your commit message.</p>
        : <div className="opc-commits-list">{autoCommits.map(c => <CommitRow key={c.hash} commit={c} onPin={() => handleLockIn(c)} isPinned={pinned.some(p => p.hash === c.hash)} onViewDiff={setViewingHash} />)}</div>
      }

      <Divider />

      {/* Pinned */}
      <div className="opc-commits-section-label">
        <span className="opc-field-label">Pinned</span>
        <span className="opc-section-hint"> — manually linked commits</span>
      </div>
      {pinned.length === 0
        ? <p className="opc-empty-hint">No commits pinned yet.</p>
        : (
          <div className="opc-commits-list">
            {pinned.map(c => (
              <div key={c.hash} className="opc-commit-row">
                <Tooltip content="View diff" placement="left">
                  <code className="opc-commit-hash" style={{ cursor: 'pointer' }}
                    onClick={() => setViewingHash(c.hash)}>
                    {c.shortHash}
                  </code>
                </Tooltip>
                <div className="opc-commit-info">
                  <div className="opc-commit-msg">{c.subject || c.hash}</div>
                  <div className="opc-commit-meta">{c.author} · pinned by {c.pinnedBy}</div>
                </div>
                <Button icon="unpin" minimal small intent={Intent.DANGER}
                  style={{ flexShrink: 0, alignSelf: 'center' }}
                  onClick={() => handleUnpin(c.hash)} />
              </div>
            ))}
          </div>
        )
      }

      <Divider />

      {/* Pin input */}
      <div className="opc-field-label" style={{ marginBottom: '0.4rem' }}>Pin a commit</div>
      {pinError && <Callout intent={Intent.DANGER} style={{ marginBottom: '0.5rem', fontSize: '0.8rem' }}>{pinError}</Callout>}
      <div style={{ display: 'flex', gap: '0.5rem' }}>
        <InputGroup fill placeholder="Enter commit hash (full or short)"
          value={pinInput} onChange={e => setPinInput(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter') handlePin(); }} />
        <Button intent={Intent.PRIMARY} text="Pin" loading={pinning}
          disabled={!pinInput.trim()} onClick={handlePin} />
      </div>

      <GitCommitDrawer hash={viewingHash} onClose={() => setViewingHash(null)} />
    </div>
  );
}

// -- Detail drawer -------------------------------------------------------------

function OpcDetailDrawer({ opc, onClose, onUpdate }: {
  opc: Opc; onClose: () => void; onUpdate: (updated: Opc) => void;
}) {
  const [notes,         setNotes]         = useState<OpcNote[]>([]);
  const [notesLoaded,   setNotesLoaded]   = useState(false);
  const [noteText,      setNoteText]      = useState('');
  const [savingNote,    setSavingNote]    = useState(false);
  const [editDesc,      setEditDesc]      = useState(opc.description);
  const [activeTab,     setActiveTab]     = useState('details');

  const handleStatusChange = async (s: OpcStatus) => {
    const updated = await updateOpc(opc.id, { status: s });
    onUpdate({ ...opc, ...updated });
  };

  const handleDescConfirm = async (val: string) => {
    const updated = await updateOpc(opc.id, { description: val });
    onUpdate({ ...opc, ...updated });
  };

  const loadNotes = useCallback(async () => {
    if (notesLoaded) return;
    setNotes(await listNotes(opc.id));
    setNotesLoaded(true);
  }, [opc.id, notesLoaded]);

  useEffect(() => { loadNotes(); }, [loadNotes]);

  const handleAddNote = async () => {
    if (!noteText.trim()) return;
    setSavingNote(true);
    try {
      const note = await addNote(opc.id, 'amadzarak', noteText.trim());
      setNotes(prev => [...prev, note]);
      setNoteText('');
    } finally { setSavingNote(false); }
  };

  const [copied, setCopied] = useState(false);
  const commitRef = `${opc.number}: ${opc.title}`;
  const handleCopy = () => {
    navigator.clipboard.writeText(commitRef).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
    });
  };

  return (
    <Drawer isOpen onClose={onClose} size="760px"
      title={
        <span style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
          <code className="opc-number-chip">{opc.number}</code>
          <span style={{ fontWeight: 600, fontSize: '0.975rem' }}>{opc.title}</span>
          <Tooltip content={copied ? 'Copied!' : 'Copy commit message'} placement="top">
            <Button
              icon={copied ? 'tick' : 'clipboard'}
              minimal
              small
              intent={copied ? Intent.SUCCESS : Intent.NONE}
              onClick={handleCopy}
              style={{ marginLeft: '0.1rem' }}
            />
          </Tooltip>
        </span>
      }
    >
      <div className="opc-drawer-body">
        <div className="opc-meta-strip">
          <Tag intent={TYPE_INTENT[opc.type]} minimal round>{TYPE_LABELS[opc.type]}</Tag>
          <Tag intent={PRIORITY_INTENT[opc.priority]} minimal>{opc.priority}</Tag>
          <div style={{ flex: 1 }} />
          <span className="opc-meta-label">Status</span>
          <HTMLSelect minimal value={opc.status}
            onChange={e => handleStatusChange(e.target.value as OpcStatus)}
            options={Object.entries(STATUS_LABELS).map(([v, l]) => ({ value: v, label: l }))} />
        </div>
        <Divider style={{ margin: '0.75rem 0' }} />

        <Tabs animate selectedTabId={activeTab} onChange={id => setActiveTab(String(id))}>

          {/* Details */}
          <Tab id="details" title="Details" panel={
            <div className="opc-tab-panel">
              <div className="opc-field-label">Description</div>
              <EditableText multiline minLines={3} maxLines={12} value={editDesc}
                onChange={setEditDesc} onConfirm={handleDescConfirm}
                placeholder="Click to add description..." className="opc-editable-desc" />
              <AiAssistBox context={editDesc || opc.title}
                onApply={t => setEditDesc(prev => prev ? `${prev}\n\n${t}` : t)} />
              <div className="opc-field-grid">
                <div><div className="opc-field-label">Assignee</div><div className="opc-field-value">{opc.assignee}</div></div>
                <div><div className="opc-field-label">Created</div><div className="opc-field-value">{fmtDate(opc.createdAt)}</div></div>
                <div><div className="opc-field-label">Updated</div><div className="opc-field-value">{fmtDate(opc.updatedAt)}</div></div>
              </div>
            </div>
          } />

          {/* Artifact tabs */}
          {ARTIFACT_TABS.map(at => (
            <Tab key={at.type} id={at.type} title={at.label} panel={
              <div className="opc-tab-panel">
                <ArtifactPanel opcId={opc.id} opcNumber={opc.number}
                  artifactType={at.type} placeholder={at.placeholder} />
              </div>
            } />
          ))}

          {/* Notes */}
          <Tab id="notes" title="Notes" panel={
            <div className="opc-tab-panel">
              <div className="opc-notes-feed">
                {!notesLoaded
                  ? <NonIdealState icon={<Spinner size={20} />} title="Loading..." />
                  : notes.length === 0
                    ? <NonIdealState icon="comment" title="No notes yet" description="Add the first note below." />
                    : notes.map(n => (
                        <div key={n.id} className="opc-note-card">
                          <div className="opc-note-header">
                            <span className="opc-note-author">{n.author}</span>
                            <span className="opc-note-time">{fmtDateTime(n.timestamp)}</span>
                          </div>
                          <div className="opc-note-content">{n.content}</div>
                        </div>
                      ))
                }
              </div>
              <div className="opc-note-compose">
                <TextArea fill rows={4} placeholder="Add a stakeholder note..."
                  value={noteText} onChange={e => setNoteText(e.target.value)} />
                <AiAssistBox context={opc.description}
                  onApply={t => setNoteText(prev => prev ? `${prev}\n\n${t}` : t)} />
                <Button intent={Intent.PRIMARY} text="Add Note" loading={savingNote}
                  style={{ marginTop: '0.5rem', alignSelf: 'flex-end' }}
                  onClick={handleAddNote} disabled={!noteText.trim()} />
              </div>
            </div>
          } />

          {/* Code & SDLC */}
          <Tab id="commits" title="Code & SDLC" panel={
            <CommitsTab opc={opc} isActive={activeTab === 'commits'} />
          } />
        </Tabs>
      </div>
    </Drawer>
  );
}

// -- Create drawer -------------------------------------------------------------

interface CreateForm {
  title: string; type: OpcType; priority: OpcPriority; assignee: string; description: string;
}

function OpcCreateDrawer({ onClose, onCreate }: { onClose: () => void; onCreate: (opc: Opc) => void }) {
  const [form,       setForm]       = useState<CreateForm>({ title: '', type: 'General', priority: 'Medium', assignee: 'amadzarak', description: '' });
  const [saving,     setSaving]     = useState(false);
  const [error,      setError]      = useState<string | null>(null);
  const [nextNumber, setNextNumber] = useState<string | null>(null);

  useEffect(() => {
    getNextNumber().then(setNextNumber).catch(() => { /* non-critical */ });
  }, []);

  const patch = <K extends keyof CreateForm>(key: K, value: CreateForm[K]) => setForm(f => ({ ...f, [key]: value }));
  const applyAi = (t: string) => patch('description', form.description ? `${form.description}\n\n${t}` : t);

  const handleCreate = async () => {
    setSaving(true); setError(null);
    try { onCreate(await createOpc(form)); }
    catch (e) { setError(String(e)); }
    finally { setSaving(false); }
  };

  return (
    <Drawer isOpen onClose={onClose} size="520px"
      title={
        <span style={{ display: 'flex', alignItems: 'center', gap: '0.6rem' }}>
          {nextNumber
            ? <code className="opc-number-chip">{nextNumber}</code>
            : <Spinner size={14} />}
          <span style={{ fontWeight: 600 }}>New OPC</span>
        </span>
      }
    >
      <div className="opc-drawer-body">
        <Callout intent={Intent.PRIMARY} icon="info-sign" style={{ marginBottom: '1.25rem' }}>
          An OPC tracks a change, task, or business requirement through its full lifecycle.
          {nextNumber && <> This will be saved as <strong>{nextNumber}</strong>.</>}{' '}
          Include the OPC number in commit messages to link check-ins automatically across any repo.
        </Callout>
        {error && <Callout intent={Intent.DANGER} style={{ marginBottom: '1rem' }}>{error}</Callout>}
        <FormGroup label="Title" labelInfo="(required)">
          <InputGroup placeholder="Short descriptive title" value={form.title}
            onChange={e => patch('title', e.target.value)} autoFocus />
        </FormGroup>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }}>
          <FormGroup label="Type">
            <HTMLSelect fill value={form.type} onChange={e => patch('type', e.target.value as OpcType)}
              options={[
                { value: 'ChangeOrder',         label: 'Change Order' },
                { value: 'NonDevTask',           label: 'Non-Dev Task' },
                { value: 'QaTask',              label: 'QA Task' },
                { value: 'BusinessRequirement', label: 'Business Requirement (JAD / ARD)' },
                { value: 'Feature',             label: 'Feature' },
                { value: 'General',             label: 'General' },
              ]} />
          </FormGroup>
          <FormGroup label="Priority">
            <HTMLSelect fill value={form.priority} onChange={e => patch('priority', e.target.value as OpcPriority)}
              options={(['Low', 'Medium', 'High', 'Critical'] as OpcPriority[]).map(v => ({ value: v, label: v }))} />
          </FormGroup>
        </div>
        <FormGroup label="Assignee">
          <InputGroup value={form.assignee} onChange={e => patch('assignee', e.target.value)} />
        </FormGroup>
        <FormGroup label="Description">
          <TextArea fill rows={6} placeholder="Goals, scope, acceptance criteria..."
            value={form.description} onChange={e => patch('description', e.target.value)} />
        </FormGroup>
        <AiAssistBox context={form.title || undefined} onApply={applyAi} />
        <div className="opc-drawer-actions">
          <Button text="Cancel" onClick={onClose} minimal />
          <Button intent={Intent.PRIMARY} text="Create OPC" loading={saving}
            disabled={!form.title.trim() || saving} onClick={handleCreate} />
        </div>
      </div>
    </Drawer>
  );
}

// -- Main page -----------------------------------------------------------------

export default function OpcPage() {
  const [opcs,         setOpcs]         = useState<Opc[]>([]);
  const [loading,      setLoading]      = useState(true);
  const [selected,     setSelected]     = useState<Opc | null>(null);
  const [creating,     setCreating]     = useState(false);
  const [search,       setSearch]       = useState('');
  const [typeFilter,   setTypeFilter]   = useState('all');
  const [statusFilter, setStatusFilter] = useState('all');

  const load = useCallback(async () => {
    setLoading(true);
    try { setOpcs(await listOpcs()); }
    catch { /* API may not be reachable in dev */ }
    finally { setLoading(false); }
  }, []);

  useEffect(() => { load(); }, [load]);

  const filtered = useMemo(() => {
    const q = search.toLowerCase();
    return opcs.filter(o => {
      if (typeFilter   !== 'all' && o.type   !== typeFilter)   return false;
      if (statusFilter !== 'all' && o.status !== statusFilter) return false;
      if (q && !o.title.toLowerCase().includes(q) && !o.number.toLowerCase().includes(q)) return false;
      return true;
    });
  }, [opcs, typeFilter, statusFilter, search]);

  const handleCreated = (opc: Opc) => { setOpcs(prev => [opc, ...prev]); setCreating(false); setSelected(opc); };
  const handleUpdated = (updated: Opc) => { setOpcs(prev => prev.map(o => o.id === updated.id ? updated : o)); setSelected(updated); };

  return (
    <div>
      <div className="page-header">
        <div>
          <h1>OPC</h1>
          <p>Online Project Communication — track changes, requirements, and SDLC delivery chain.</p>
        </div>
        <Button intent={Intent.PRIMARY} icon="plus" text="New OPC"
          onClick={() => { setSelected(null); setCreating(true); }} />
      </div>

      <div className="opc-filter-bar">
        <InputGroup leftIcon="search" placeholder="Search OPCs..." value={search}
          onChange={e => setSearch(e.target.value)} style={{ width: 260 }} />
        <HTMLSelect value={typeFilter} onChange={e => setTypeFilter(e.target.value)}
          options={[
            { value: 'all',                 label: 'All Types' },
            { value: 'ChangeOrder',          label: 'Change Orders' },
            { value: 'NonDevTask',           label: 'Non-Dev Tasks' },
            { value: 'QaTask',              label: 'QA Tasks' },
            { value: 'BusinessRequirement', label: 'Business Requirements' },
            { value: 'Feature',             label: 'Features' },
            { value: 'General',             label: 'General' },
          ]} />
        <HTMLSelect value={statusFilter} onChange={e => setStatusFilter(e.target.value)}
          options={[
            { value: 'all', label: 'All Statuses' },
            ...Object.entries(STATUS_LABELS).map(([v, l]) => ({ value: v, label: l })),
          ]} />
        <Button icon="refresh" minimal onClick={load} />
        <span className="opc-count-badge">{filtered.length} OPC{filtered.length !== 1 ? 's' : ''}</span>
      </div>

      {loading ? (
        <NonIdealState icon={<Spinner />} title="Loading OPCs..." />
      ) : filtered.length === 0 ? (
        <NonIdealState icon="search" title="No OPCs match" description="Adjust your filters or create a new OPC." />
      ) : (
        <HTMLTable className="opc-table" interactive striped bordered>
          <thead>
            <tr>
              <th>Number</th><th>Title</th><th>Type</th><th>Priority</th><th>Status</th><th>Assignee</th><th>Updated</th>
            </tr>
          </thead>
          <tbody>
            {filtered.map(o => (
              <tr key={o.id} onClick={() => { setCreating(false); setSelected(o); }}
                style={{ cursor: 'pointer' }} className={selected?.id === o.id ? 'opc-row-selected' : undefined}>
                <td><code className="opc-number-chip">{o.number}</code></td>
                <td className="opc-title-cell">{o.title}</td>
                <td><Tag intent={TYPE_INTENT[o.type]} minimal round>{TYPE_LABELS[o.type]}</Tag></td>
                <td><Tag intent={PRIORITY_INTENT[o.priority]} minimal>{o.priority}</Tag></td>
                <td><Tag intent={STATUS_INTENT[o.status]} round>{STATUS_LABELS[o.status]}</Tag></td>
                <td>{o.assignee}</td>
                <td className="opc-date-cell">{fmtDate(o.updatedAt)}</td>
              </tr>
            ))}
          </tbody>
        </HTMLTable>
      )}

      {selected && !creating && (
        <OpcDetailDrawer opc={selected} onClose={() => setSelected(null)} onUpdate={handleUpdated} />
      )}
      {creating && (
        <OpcCreateDrawer onClose={() => setCreating(false)} onCreate={handleCreated} />
      )}
    </div>
  );
}
