'use client';

import { Suspense, useDeferredValue, useEffect, useMemo, useState } from 'react';
import { format } from 'date-fns';
import { useQueryClient } from '@tanstack/react-query';
import { BookOpenText, Download, FilePlus2, Filter, FolderUp, Link2, NotebookPen, Search, ShieldCheck, Sparkles, Trash2 } from 'lucide-react';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
import { ResearchCopilotPanel } from '@/components/research/research-copilot-panel';
import { AppShell } from '@/components/shell/app-shell';
import { Panel } from '@/components/ui/panel';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useAuthGuard } from '@/hooks/use-auth-guard';
import { useLinkPrefetch } from '@/hooks/use-link-prefetch';
import {
  addResearchMemoEvidence,
  createResearchArtifact,
  deleteResearchArtifact,
  deleteResearchMemoEvidence,
  getResearchArtifactFileUrl,
  updateResearchArtifact,
  uploadResearchArtifact,
  upsertResearchMemo
} from '@/lib/api';
import { queryKeys } from '@/lib/query/keys';
import {
  researchLibraryQueryOptions,
  researchWorkspaceQueryOptions
} from '@/lib/query/options';
import type {
  ResearchArtifact,
  ResearchArtifactKind,
  ResearchArtifactSource,
  ResearchCopilotCitation,
  ResearchCopilotSession,
  ResearchMemo,
  ResearchMemoSection,
  ResearchWorkspace
} from '@/lib/types';

const MEMO_SECTIONS: Array<{ value: ResearchMemoSection; label: string }> = [
  { value: 'thesis', label: 'Thesis' },
  { value: 'variant_view', label: 'Variant View' },
  { value: 'catalysts', label: 'Catalysts' },
  { value: 'risks', label: 'Risks' },
  { value: 'disconfirming_evidence', label: 'Disconfirming Evidence' },
  { value: 'next_actions', label: 'Next Actions' }
];

const KIND_OPTIONS: Array<{ value: '' | ResearchArtifactKind; label: string }> = [
  { value: '', label: 'All artifacts' },
  { value: 'note', label: 'Notes' },
  { value: 'ai_report', label: 'AI memos' },
  { value: 'filing', label: 'Filings' },
  { value: 'upload', label: 'Uploads' },
  { value: 'status_change', label: 'Status events' }
];

const SOURCE_OPTIONS: Array<{ value: '' | ResearchArtifactSource; label: string }> = [
  { value: '', label: 'All sources' },
  { value: 'user', label: 'User-authored' },
  { value: 'system', label: 'System-generated' }
];

type NoteFormState = {
  id: number | null;
  title: string;
  summary: string;
  bodyMarkdown: string;
  tags: string;
};

type MemoFormState = {
  rating: string;
  conviction: string;
  timeHorizonMonths: string;
  packetTitle: string;
  packetSubtitle: string;
  thesisMarkdown: string;
  variantViewMarkdown: string;
  catalystsMarkdown: string;
  risksMarkdown: string;
  disconfirmingEvidenceMarkdown: string;
  nextActionsMarkdown: string;
};

const EMPTY_NOTE_FORM: NoteFormState = {
  id: null,
  title: '',
  summary: '',
  bodyMarkdown: '',
  tags: ''
};

const EMPTY_MEMO_FORM: MemoFormState = {
  rating: '',
  conviction: '',
  timeHorizonMonths: '',
  packetTitle: '',
  packetSubtitle: '',
  thesisMarkdown: '',
  variantViewMarkdown: '',
  catalystsMarkdown: '',
  risksMarkdown: '',
  disconfirmingEvidenceMarkdown: '',
  nextActionsMarkdown: ''
};

const MEMO_FORM_FIELD_BY_SECTION: Record<ResearchMemoSection, keyof MemoFormState> = {
  thesis: 'thesisMarkdown',
  variant_view: 'variantViewMarkdown',
  catalysts: 'catalystsMarkdown',
  risks: 'risksMarkdown',
  disconfirming_evidence: 'disconfirmingEvidenceMarkdown',
  next_actions: 'nextActionsMarkdown'
};

function parseTags(value: string) {
  const unique = new Set<string>();

  for (const segment of value.split(',')) {
    const normalized = segment.trim();
    if (!normalized) {
      continue;
    }

    unique.add(normalized);
  }

  return [...unique];
}

function tagsToInput(tags: string[]) {
  return tags.join(', ');
}

function formatTimestamp(value: string | null | undefined) {
  if (!value) {
    return 'n/a';
  }

  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return 'n/a';
  }

  return format(date, 'MMM dd, yyyy · HH:mm');
}

function toMemoForm(memo: ResearchMemo | null): MemoFormState {
  if (!memo) {
    return EMPTY_MEMO_FORM;
  }

  return {
    rating: memo.rating ?? '',
    conviction: memo.conviction ?? '',
    timeHorizonMonths: memo.time_horizon_months ? String(memo.time_horizon_months) : '',
    packetTitle: memo.packet_title ?? '',
    packetSubtitle: memo.packet_subtitle ?? '',
    thesisMarkdown: memo.thesis_markdown,
    variantViewMarkdown: memo.variant_view_markdown,
    catalystsMarkdown: memo.catalysts_markdown,
    risksMarkdown: memo.risks_markdown,
    disconfirmingEvidenceMarkdown: memo.disconfirming_evidence_markdown,
    nextActionsMarkdown: memo.next_actions_markdown
  };
}

function noteFormFromArtifact(artifact: ResearchArtifact): NoteFormState {
  return {
    id: artifact.id,
    title: artifact.title ?? '',
    summary: artifact.summary ?? '',
    bodyMarkdown: artifact.body_markdown ?? '',
    tags: tagsToInput(artifact.tags)
  };
}

export default function ResearchPage() {
  return (
    <Suspense fallback={<div className="flex min-h-screen items-center justify-center text-sm text-[color:var(--terminal-muted)]">Loading research workspace...</div>}>
      <ResearchPageContent />
    </Suspense>
  );
}

function ResearchPageContent() {
  const { isPending, isAuthenticated } = useAuthGuard();
  const searchParams = useSearchParams();
  const queryClient = useQueryClient();
  const { prefetchResearchTicker } = useLinkPrefetch();

  const [workspace, setWorkspace] = useState<ResearchWorkspace | null>(null);
  const [library, setLibrary] = useState<ResearchArtifact[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  const [noteForm, setNoteForm] = useState<NoteFormState>(EMPTY_NOTE_FORM);
  const [memoForm, setMemoForm] = useState<MemoFormState>(EMPTY_MEMO_FORM);
  const [searchInput, setSearchInput] = useState('');
  const [kindFilter, setKindFilter] = useState<'' | ResearchArtifactKind>('');
  const [sourceFilter, setSourceFilter] = useState<'' | ResearchArtifactSource>('');
  const [tagFilter, setTagFilter] = useState('');
  const [linkedOnly, setLinkedOnly] = useState(false);
  const [attachSection, setAttachSection] = useState<ResearchMemoSection>('thesis');
  const [uploadFile, setUploadFile] = useState<File | null>(null);
  const [uploadTitle, setUploadTitle] = useState('');
  const [uploadSummary, setUploadSummary] = useState('');
  const [uploadTags, setUploadTags] = useState('');
  const [researchMode, setResearchMode] = useState<'workspace' | 'copilot'>('workspace');
  const [focusTab, setFocusTab] = useState<'library' | 'memo' | 'packet'>('library');

  const ticker = useMemo(() => searchParams.get('ticker')?.trim().toUpperCase() ?? '', [searchParams]);
  const deferredSearch = useDeferredValue(searchInput);

  const loadWorkspace = async (symbol: string) => {
    if (!symbol) {
      setWorkspace(null);
      setLibrary([]);
      setLoading(false);
      return;
    }

    const options = researchWorkspaceQueryOptions(symbol);
    if (!queryClient.getQueryData(options.queryKey)) {
      setLoading(true);
    }

    try {
      const response = await queryClient.ensureQueryData(options);
      setWorkspace(response.workspace);
      setLibrary(response.workspace.library);
      setMemoForm(toMemoForm(response.workspace.memo));
      setError(null);
    } catch (err) {
      setWorkspace(null);
      setLibrary([]);
      setError(err instanceof Error ? err.message : 'Unable to load research workspace');
    } finally {
      setLoading(false);
    }
  };

  const loadLibrary = async (symbol: string) => {
    if (!symbol) {
      return;
    }

    try {
      const response = await queryClient.fetchQuery(researchLibraryQueryOptions({
        ticker: symbol,
        q: deferredSearch,
        kind: kindFilter || undefined,
        source: sourceFilter || undefined,
        tag: tagFilter || undefined,
        linkedToMemo: linkedOnly ? true : undefined,
        limit: 100
      }));
      setLibrary(response.artifacts);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unable to load filtered library');
    }
  };

  const invalidateResearch = async (symbol: string) => {
    await Promise.all([
      queryClient.invalidateQueries({ queryKey: queryKeys.researchWorkspace(symbol) }),
      queryClient.invalidateQueries({ queryKey: queryKeys.researchCopilotSession(symbol) }),
      queryClient.invalidateQueries({ queryKey: ['research', 'library', symbol] }),
      queryClient.invalidateQueries({ queryKey: queryKeys.researchMemo(symbol) }),
      queryClient.invalidateQueries({ queryKey: queryKeys.researchPacket(symbol) }),
      queryClient.invalidateQueries({ queryKey: queryKeys.researchJournal(symbol) }),
      queryClient.invalidateQueries({ queryKey: queryKeys.companyAnalysis(symbol) }),
      queryClient.invalidateQueries({ queryKey: queryKeys.watchlist() })
    ]);
  };

  useEffect(() => {
    if (!isPending && isAuthenticated) {
      void loadWorkspace(ticker);
    }
  }, [isPending, isAuthenticated, ticker]);

  useEffect(() => {
    if (!workspace) {
      return;
    }

    void loadLibrary(workspace.ticker);
  }, [workspace?.ticker, deferredSearch, kindFilter, sourceFilter, tagFilter, linkedOnly]);

  if (isPending || !isAuthenticated) {
    return <div className="flex min-h-screen items-center justify-center text-sm text-[color:var(--terminal-muted)]">Loading research workspace...</div>;
  }

  const saveNote = async () => {
    if (!ticker) {
      return;
    }

    try {
      if (noteForm.id === null) {
        await createResearchArtifact({
          ticker,
          kind: 'note',
          source: 'user',
          title: noteForm.title || undefined,
          summary: noteForm.summary || undefined,
          bodyMarkdown: noteForm.bodyMarkdown || undefined,
          tags: parseTags(noteForm.tags)
        });
        setNotice('Saved note to the research library.');
      } else {
        await updateResearchArtifact(noteForm.id, {
          title: noteForm.title || undefined,
          summary: noteForm.summary || undefined,
          bodyMarkdown: noteForm.bodyMarkdown || undefined,
          tags: parseTags(noteForm.tags)
        });
        setNotice('Updated research note.');
      }

      setNoteForm(EMPTY_NOTE_FORM);
      await invalidateResearch(ticker);
      await loadWorkspace(ticker);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unable to save note');
    }
  };

  const saveMemo = async () => {
    if (!ticker) {
      return;
    }

    try {
      await upsertResearchMemo({
        ticker,
        rating: memoForm.rating ? memoForm.rating as ResearchMemo['rating'] : null,
        conviction: memoForm.conviction ? memoForm.conviction as ResearchMemo['conviction'] : null,
        timeHorizonMonths: memoForm.timeHorizonMonths ? Number(memoForm.timeHorizonMonths) : null,
        packetTitle: memoForm.packetTitle || undefined,
        packetSubtitle: memoForm.packetSubtitle || undefined,
        thesisMarkdown: memoForm.thesisMarkdown,
        variantViewMarkdown: memoForm.variantViewMarkdown,
        catalystsMarkdown: memoForm.catalystsMarkdown,
        risksMarkdown: memoForm.risksMarkdown,
        disconfirmingEvidenceMarkdown: memoForm.disconfirmingEvidenceMarkdown,
        nextActionsMarkdown: memoForm.nextActionsMarkdown
      });
      setNotice('Saved investment memo.');
      await invalidateResearch(ticker);
      await loadWorkspace(ticker);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unable to save investment memo');
    }
  };

  const uploadFileToLibrary = async () => {
    if (!ticker || !uploadFile) {
      return;
    }

    try {
      await uploadResearchArtifact({
        ticker,
        file: uploadFile,
        title: uploadTitle || undefined,
        summary: uploadSummary || undefined,
        tags: parseTags(uploadTags)
      });
      setUploadFile(null);
      setUploadTitle('');
      setUploadSummary('');
      setUploadTags('');
      setNotice('Uploaded research file.');
      await invalidateResearch(ticker);
      await loadWorkspace(ticker);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unable to upload research file');
    }
  };

  const ensureMemo = async () => {
    if (!ticker) {
      throw new Error('Ticker is required');
    }

    if (workspace?.memo) {
      return workspace.memo.id;
    }

    const response = await upsertResearchMemo({ ticker });
    await invalidateResearch(ticker);
    await loadWorkspace(ticker);
    return response.memo.id;
  };

  const attachArtifact = async (artifact: ResearchArtifact) => {
    try {
      const memoId = await ensureMemo();
      await addResearchMemoEvidence({
        memoId,
        artifactId: artifact.id,
        section: attachSection
      });
      setNotice(`Attached evidence to ${MEMO_SECTIONS.find((item) => item.value === attachSection)?.label}.`);
      await invalidateResearch(ticker);
      await loadWorkspace(ticker);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unable to attach evidence');
    }
  };

  const handleCopilotSessionChange = (session: ResearchCopilotSession) => {
    setWorkspace((current) => current ? { ...current, copilotSession: session } : current);
  };

  const handleDraftNote = (input: { title: string; summary: string; bodyMarkdown: string }) => {
    setNoteForm({
      id: null,
      title: input.title,
      summary: input.summary,
      bodyMarkdown: input.bodyMarkdown,
      tags: 'copilot'
    });
    setNotice('Loaded copilot output into the note draft editor for review.');
  };

  const handleDraftMemoSection = (section: ResearchMemoSection, contentMarkdown: string) => {
    const field = MEMO_FORM_FIELD_BY_SECTION[section];
    setMemoForm((current) => ({
      ...current,
      [field]: contentMarkdown
    }));
    setAttachSection(section);
    setNotice(`Loaded copilot output into ${MEMO_SECTIONS.find((item) => item.value === section)?.label}.`);
  };

  const handleAttachCitation = async (citation: ResearchCopilotCitation, section: ResearchMemoSection) => {
    if (!citation.artifactId) {
      setError('This citation cannot be attached because no research artifact is available for it.');
      return;
    }

    try {
      const memoId = await ensureMemo();
      await addResearchMemoEvidence({
        memoId,
        artifactId: citation.artifactId,
        section
      });
      setNotice(`Attached cited evidence to ${MEMO_SECTIONS.find((item) => item.value === section)?.label}.`);
      await invalidateResearch(ticker);
      await loadWorkspace(ticker);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unable to attach cited evidence');
    }
  };

  const availableTags = workspace?.availableTags ?? [];
  const memoEvidenceCount = workspace?.packet.sections.reduce((sum, section) => sum + section.evidence.length, 0) ?? 0;

  const renderQuickNotePanel = () => (
    <Panel
      title={noteForm.id === null ? 'Quick Note' : 'Edit Note'}
      subtitle="Capture thesis changes, diligence notes, and interpretation gaps directly into the library."
      actions={<NotebookPen className="size-4 text-[color:var(--accent)]" />}
    >
      <div className="space-y-3">
        <Input aria-label="Research note title" value={noteForm.title} onChange={(event) => setNoteForm((current) => ({ ...current, title: event.target.value }))} placeholder="Headline or checkpoint title" />
        <Input aria-label="Research note summary" value={noteForm.summary} onChange={(event) => setNoteForm((current) => ({ ...current, summary: event.target.value }))} placeholder="One-line summary for skimming and search" />
        <textarea
          aria-label="Research note body"
          value={noteForm.bodyMarkdown}
          onChange={(event) => setNoteForm((current) => ({ ...current, bodyMarkdown: event.target.value }))}
          placeholder="Write the actual research note, variant view, or diligence conclusion..."
          className="min-h-[160px] w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)] outline-none transition placeholder:text-[color:var(--terminal-muted)] focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_var(--focus-ring)]"
        />
        <Input aria-label="Research note tags" value={noteForm.tags} onChange={(event) => setNoteForm((current) => ({ ...current, tags: event.target.value }))} placeholder="Tags, comma-separated" />
        <div className="flex flex-wrap gap-2">
          <Button onClick={() => void saveNote()}>
            <FilePlus2 className="size-4" />
            {noteForm.id === null ? 'Save note' : 'Update note'}
          </Button>
          {noteForm.id !== null ? (
            <Button variant="ghost" onClick={() => setNoteForm(EMPTY_NOTE_FORM)}>
              Cancel edit
            </Button>
          ) : null}
        </div>
      </div>
    </Panel>
  );

  const renderLibraryPanel = () => (
    <Panel
      title="Research Library"
      subtitle={`${library.length} artifacts match the current filter set.`}
      actions={(
        <div className="flex items-center gap-2">
          <span className="text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">Attach to</span>
          <select className="rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-2 py-1 text-xs text-[color:var(--terminal-bright)]" value={attachSection} onChange={(event) => setAttachSection(event.target.value as ResearchMemoSection)}>
            {MEMO_SECTIONS.map((section) => (
              <option key={section.value} value={section.value}>{section.label}</option>
            ))}
          </select>
        </div>
      )}
    >
      <div className="space-y-4">
        <div className="grid gap-3 rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.03)] p-4 lg:grid-cols-[1.4fr_repeat(3,minmax(0,1fr))]">
          <div>
            <label className="mb-1 block text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">Search</label>
            <div className="relative">
              <Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-[color:var(--terminal-muted)]" />
              <Input aria-label="Research search" className="pl-9" value={searchInput} onChange={(event) => setSearchInput(event.target.value)} placeholder="Keyword search research..." />
            </div>
          </div>
          <div>
            <label className="mb-1 block text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">Artifact Type</label>
            <select aria-label="Artifact type filter" className="w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]" value={kindFilter} onChange={(event) => setKindFilter(event.target.value as '' | ResearchArtifactKind)}>
              {KIND_OPTIONS.map((option) => (
                <option key={option.label} value={option.value}>{option.label}</option>
              ))}
            </select>
          </div>
          <div>
            <label className="mb-1 block text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">Source</label>
            <select aria-label="Artifact source filter" className="w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]" value={sourceFilter} onChange={(event) => setSourceFilter(event.target.value as '' | ResearchArtifactSource)}>
              {SOURCE_OPTIONS.map((option) => (
                <option key={option.label} value={option.value}>{option.label}</option>
              ))}
            </select>
          </div>
          <div>
            <label className="mb-1 block text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">Tag</label>
            <select aria-label="Artifact tag filter" className="w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]" value={tagFilter} onChange={(event) => setTagFilter(event.target.value)}>
              <option value="">All tags</option>
              {availableTags.map((tag) => (
                <option key={tag} value={tag}>{tag}</option>
              ))}
            </select>
          </div>
        </div>

        <div className="flex flex-wrap items-center gap-3">
          <label className="flex items-center gap-2 rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]">
            <input type="checkbox" checked={linkedOnly} onChange={(event) => setLinkedOnly(event.target.checked)} />
            Show memo-linked evidence only
          </label>
          <div className="flex items-center gap-2 rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-xs text-[color:var(--terminal-muted)]">
            <ShieldCheck className="size-4 text-[color:var(--accent)]" />
            Private workspace scope
          </div>
        </div>

        <div className="space-y-3">
          {library.length === 0 ? (
            <p className="text-sm text-[color:var(--terminal-muted)]">No artifacts match the current search and filter combination.</p>
          ) : (
            library.map((artifact) => (
              <article key={artifact.id} className="rounded-2xl border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] p-4">
                <div className="flex flex-wrap items-start justify-between gap-3">
                  <div>
                    <p className="text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">
                      {artifact.kind.replace('_', ' ')} · {artifact.source} · {formatTimestamp(artifact.updated_at)}
                    </p>
                    <h4 className="mt-1 text-sm font-semibold text-[color:var(--terminal-bright)]">
                      {artifact.title ?? `${artifact.kind.replace('_', ' ')} artifact`}
                    </h4>
                  </div>
                  <div className="flex flex-wrap gap-2">
                    {artifact.kind === 'upload' && artifact.storage_path ? (
                      <a className="inline-flex items-center gap-1 rounded-lg border border-[color:var(--line-weak)] px-2 py-1 text-xs text-[color:var(--accent)] transition hover:border-[color:var(--line-strong)]" href={getResearchArtifactFileUrl(artifact.id)}>
                        <Download className="size-3" />
                        File
                      </a>
                    ) : null}
                    <Button variant="ghost" className="px-2 py-1 text-xs" onClick={() => void attachArtifact(artifact)}>
                      <Link2 className="size-3" />
                      Attach
                    </Button>
                    {artifact.kind === 'note' ? (
                      <Button variant="ghost" className="px-2 py-1 text-xs" onClick={() => setNoteForm(noteFormFromArtifact(artifact))}>
                        Edit
                      </Button>
                    ) : null}
                    {artifact.source === 'user' || artifact.kind === 'upload' ? (
                      <Button
                        variant="danger"
                        className="px-2 py-1 text-xs"
                        onClick={async () => {
                          try {
                            await deleteResearchArtifact(artifact.id);
                            setNotice('Removed artifact from the library.');
                            await invalidateResearch(ticker);
                            await loadWorkspace(ticker);
                          } catch (err) {
                            setError(err instanceof Error ? err.message : 'Unable to delete artifact');
                          }
                        }}
                      >
                        <Trash2 className="size-3" />
                        Delete
                      </Button>
                    ) : null}
                  </div>
                </div>
                {artifact.summary ? (
                  <p className="mt-3 text-sm leading-6 text-[color:var(--terminal-bright)]">{artifact.summary}</p>
                ) : null}
                {artifact.body_markdown ? (
                  <p className="mt-3 whitespace-pre-wrap text-sm leading-6 text-[color:var(--terminal-muted)]">{artifact.body_markdown}</p>
                ) : null}
                <div className="mt-4 flex flex-wrap gap-2">
                  {artifact.linked_to_memo ? (
                    <span className="rounded-full border border-[color:var(--line-strong)] px-2 py-1 text-[10px] uppercase tracking-[0.14em] text-[color:var(--accent)]">In memo</span>
                  ) : null}
                  {artifact.accession_number ? (
                    <span className="rounded-full border border-[color:var(--line-weak)] px-2 py-1 text-[10px] uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">{artifact.accession_number}</span>
                  ) : null}
                  {artifact.tags.map((tag) => (
                    <button
                      key={`${artifact.id}-${tag}`}
                      type="button"
                      className="rounded-full border border-[color:var(--line-weak)] px-2 py-1 text-[10px] uppercase tracking-[0.14em] text-[color:var(--terminal-muted)] transition hover:border-[color:var(--line-strong)]"
                      onClick={() => setTagFilter(tag)}
                    >
                      {tag}
                    </button>
                  ))}
                </div>
              </article>
            ))
          )}
        </div>
      </div>
    </Panel>
  );

  const renderUploadPanel = () => (
    <Panel
      title="Upload Research"
      subtitle="Store decks, transcripts, channel-check notes, and internal models with metadata-first handling."
      actions={<FolderUp className="size-4 text-[color:var(--accent)]" />}
    >
      <div className="space-y-3">
        <Input aria-label="Upload title" value={uploadTitle} onChange={(event) => setUploadTitle(event.target.value)} placeholder="Optional display title" />
        <Input aria-label="Upload summary" value={uploadSummary} onChange={(event) => setUploadSummary(event.target.value)} placeholder="Optional file summary" />
        <Input aria-label="Upload tags" value={uploadTags} onChange={(event) => setUploadTags(event.target.value)} placeholder="Tags, comma-separated" />
        <input
          aria-label="Upload file"
          type="file"
          onChange={(event) => setUploadFile(event.target.files?.[0] ?? null)}
          className="block w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]"
        />
        <Button variant="secondary" onClick={() => void uploadFileToLibrary()} disabled={!uploadFile}>
          <UploadIcon />
          Upload file
        </Button>
      </div>
    </Panel>
  );

  const renderMemoPanel = () => (
    <Panel
      title="Investment Memo"
      subtitle="This is the living buy-side thesis. Use the library to attach evidence into sections before packet review."
      actions={<BookOpenText className="size-4 text-[color:var(--accent)]" />}
    >
      <div className="space-y-4">
        <div className="grid gap-3 md:grid-cols-2">
          <select aria-label="Memo rating" className="rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]" value={memoForm.rating} onChange={(event) => setMemoForm((current) => ({ ...current, rating: event.target.value }))}>
            <option value="">Rating</option>
            <option value="strong_buy">Strong Buy</option>
            <option value="buy">Buy</option>
            <option value="hold">Hold</option>
            <option value="sell">Sell</option>
          </select>
          <select aria-label="Memo conviction" className="rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]" value={memoForm.conviction} onChange={(event) => setMemoForm((current) => ({ ...current, conviction: event.target.value }))}>
            <option value="">Conviction</option>
            <option value="high">High</option>
            <option value="medium">Medium</option>
            <option value="low">Low</option>
          </select>
        </div>
        <div className="grid gap-3 md:grid-cols-2">
          <Input aria-label="Memo time horizon" value={memoForm.timeHorizonMonths} onChange={(event) => setMemoForm((current) => ({ ...current, timeHorizonMonths: event.target.value }))} placeholder="Time horizon in months" />
          <Input aria-label="Packet title" value={memoForm.packetTitle} onChange={(event) => setMemoForm((current) => ({ ...current, packetTitle: event.target.value }))} placeholder="Packet title override" />
        </div>
        <Input aria-label="Packet subtitle" value={memoForm.packetSubtitle} onChange={(event) => setMemoForm((current) => ({ ...current, packetSubtitle: event.target.value }))} placeholder="Packet subtitle" />
        {MEMO_SECTIONS.map((section) => {
          const field = MEMO_FORM_FIELD_BY_SECTION[section.value];

          return (
            <div key={section.value}>
              <label className="mb-1 block text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">{section.label}</label>
              <textarea
                aria-label={`Memo ${section.label}`}
                value={memoForm[field]}
                onChange={(event) => setMemoForm((current) => ({ ...current, [field]: event.target.value }))}
                className="min-h-[108px] w-full rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-bright)] outline-none transition placeholder:text-[color:var(--terminal-muted)] focus:border-[color:var(--line-strong)] focus:shadow-[0_0_0_3px_var(--focus-ring)]"
                placeholder={`Write ${section.label.toLowerCase()}...`}
              />
            </div>
          );
        })}
        <Button onClick={() => void saveMemo()}>
          <NotebookPen className="size-4" />
          Save memo
        </Button>
      </div>
    </Panel>
  );

  const renderPacketPanel = () => (
    <Panel
      title="Research Packet"
      subtitle="Presentation-ready memo sections with attached evidence for quick PM or IC review."
      actions={<Sparkles className="size-4 text-[color:var(--accent)]" />}
    >
      <div className="space-y-6">
        {workspace!.packet.sections.map((section) => (
          <section key={section.section} className="rounded-2xl border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] p-4">
            <div className="flex flex-wrap items-start justify-between gap-3">
              <div>
                <p className="text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">Packet Section</p>
                <h3 className="mt-1 text-lg font-semibold text-[color:var(--terminal-bright)]">{section.title}</h3>
              </div>
              <p className="text-xs uppercase tracking-[0.16em] text-[color:var(--terminal-muted)]">{section.evidence.length} evidence items</p>
            </div>
            <p className="mt-4 whitespace-pre-wrap text-sm leading-7 text-[color:var(--terminal-bright)]">
              {section.body_markdown || 'No memo content yet for this section.'}
            </p>
            {section.evidence.length > 0 ? (
              <div className="mt-4 grid gap-3 lg:grid-cols-2">
                {section.evidence.map((item) => (
                  <article key={item.id} className="rounded-xl border border-[color:var(--line-weak)] bg-[color:var(--panel)] p-3">
                    <div className="flex items-start justify-between gap-3">
                      <div>
                        <p className="text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">{item.artifact.kind.replace('_', ' ')}</p>
                        <h4 className="mt-1 text-sm font-semibold text-[color:var(--terminal-bright)]">{item.artifact.title ?? 'Untitled evidence'}</h4>
                      </div>
                      {workspace!.memo ? (
                        <Button
                          variant="ghost"
                          className="px-2 py-1 text-xs"
                          onClick={async () => {
                            try {
                              await deleteResearchMemoEvidence(workspace!.memo!.id, item.id);
                              setNotice('Removed memo evidence.');
                              await invalidateResearch(ticker);
                              await loadWorkspace(ticker);
                            } catch (err) {
                              setError(err instanceof Error ? err.message : 'Unable to remove memo evidence');
                            }
                          }}
                        >
                          Remove
                        </Button>
                      ) : null}
                    </div>
                    {item.annotation ? (
                      <p className="mt-2 text-sm text-[color:var(--terminal-muted)]">{item.annotation}</p>
                    ) : null}
                    <p className="mt-2 text-sm text-[color:var(--terminal-bright)]">{item.artifact.summary ?? item.artifact.body_markdown ?? 'No summary available.'}</p>
                  </article>
                ))}
              </div>
            ) : null}
          </section>
        ))}
      </div>
    </Panel>
  );

  return (
    <AppShell
      title="Research"
      subtitle="Build an investor-grade company dossier with evidence-linked memo sections, uploads, and a packet view."
      activeTicker={ticker || null}
      breadcrumbs={[
        { label: 'Overview', href: ticker ? `/analysis?ticker=${encodeURIComponent(ticker)}` : '/analysis' },
        { label: 'Research' }
      ]}
      actions={(
        <div className="flex flex-wrap gap-2">
          <div className="inline-flex rounded-xl border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] p-1">
            <button
              type="button"
              className={researchMode === 'workspace'
                ? 'rounded-lg bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]'
                : 'rounded-lg px-3 py-2 text-sm text-[color:var(--terminal-muted)] transition hover:text-[color:var(--terminal-bright)]'
              }
              onClick={() => setResearchMode('workspace')}
            >
              Workspace
            </button>
            <button
              type="button"
              className={researchMode === 'copilot'
                ? 'rounded-lg bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]'
                : 'rounded-lg px-3 py-2 text-sm text-[color:var(--terminal-muted)] transition hover:text-[color:var(--terminal-bright)]'
              }
              onClick={() => setResearchMode('copilot')}
            >
              Copilot Focus
            </button>
          </div>
          <Button
            variant="secondary"
            onClick={() => {
              void invalidateResearch(ticker);
              void loadWorkspace(ticker);
            }}
          >
            <Sparkles className="size-4" />
            Refresh
          </Button>
          {ticker ? (
            <Link
              href={`/analysis?ticker=${encodeURIComponent(ticker)}`}
              onMouseEnter={() => prefetchResearchTicker(ticker)}
              onFocus={() => prefetchResearchTicker(ticker)}
              className="inline-flex items-center gap-2 rounded-lg border border-[color:var(--line-weak)] px-3 py-2 text-sm text-[color:var(--accent)] transition hover:border-[color:var(--line-strong)] hover:text-[color:var(--accent-strong)]"
            >
              Open overview
            </Link>
          ) : null}
        </div>
      )}
    >
      {!ticker ? (
        <Panel title="Select a company" subtitle="Open this page with `?ticker=NVDA` or use the Coverage and Overview surfaces to pivot into research.">
          <p className="text-sm text-[color:var(--terminal-muted)]">This workspace is company-first by design and activates once a ticker is selected.</p>
        </Panel>
      ) : null}

      {ticker && workspace ? (
        <>
          <Panel className="overflow-hidden border-[color:var(--line-strong)] bg-[linear-gradient(135deg,rgba(29,31,36,0.97),rgba(34,37,42,0.9)_45%,rgba(42,46,52,0.94))]">
            <div className="grid gap-5 lg:grid-cols-[1.6fr_1fr]">
              <div>
                <p className="text-xs uppercase tracking-[0.22em] text-[color:var(--accent)]">Buy-Side Research Workspace</p>
                <h2 className="mt-3 text-3xl font-semibold text-[color:var(--terminal-bright)]">{workspace.companyName ?? workspace.ticker}</h2>
                <p className="mt-2 text-sm text-[color:var(--terminal-muted)]">
                  {workspace.coverage?.status ? `Coverage: ${workspace.coverage.status}` : 'Not yet on the coverage board'} ·
                  {' '}Last filing: {workspace.latestFilingDate ? formatTimestamp(workspace.latestFilingDate).split(' · ')[0] : 'n/a'} ·
                  {' '}Private by default
                </p>
                <div className="mt-4 flex flex-wrap gap-2">
                  {(workspace.coverage?.tags ?? []).map((tag) => (
                    <span key={tag} className="rounded-full border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.03)] px-3 py-1 text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">
                      {tag}
                    </span>
                  ))}
                </div>
              </div>
              <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-1">
                <div className="rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.04)] p-4">
                  <p className="text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">Memo posture</p>
                  <p className="mt-2 text-lg font-semibold text-[color:var(--terminal-bright)]">
                    {workspace.memo?.rating ? workspace.memo.rating.replace('_', ' ') : 'Unrated'}
                  </p>
                  <p className="mt-1 text-sm text-[color:var(--terminal-muted)]">
                    Conviction: {workspace.memo?.conviction ?? 'unset'}
                  </p>
                </div>
                <div className="rounded-2xl border border-[color:var(--line-weak)] bg-[rgba(255,255,255,0.04)] p-4">
                  <p className="text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">Research depth</p>
                  <p className="mt-2 text-lg font-semibold text-[color:var(--terminal-bright)]">{workspace.library.length} artifacts</p>
                  <p className="mt-1 text-sm text-[color:var(--terminal-muted)]">{memoEvidenceCount} evidence links in the packet</p>
                </div>
              </div>
            </div>
          </Panel>

          {notice ? (
            <Panel className="border-[color:var(--line-strong)] bg-[color:var(--panel-soft)]">
              <p className="text-sm text-[color:var(--accent)]">{notice}</p>
            </Panel>
          ) : null}

          {error ? (
            <Panel>
              <p className="text-sm text-[#ffb5b5]">{error}</p>
            </Panel>
          ) : null}

          {loading ? (
            <Panel>
              <p className="text-sm text-[color:var(--terminal-muted)]">Loading research workspace...</p>
            </Panel>
          ) : (
            <>
              {researchMode === 'workspace' ? (
                <div className="grid gap-6 xl:grid-cols-[minmax(0,1.7fr)_minmax(22rem,0.85fr)]">
                  <div className="space-y-6">
                    <div className="grid gap-6 xl:grid-cols-[1.05fr_0.95fr]">
                      <div className="space-y-6">
                        {renderQuickNotePanel()}
                        {renderLibraryPanel()}
                        {renderUploadPanel()}
                      </div>
                      {renderMemoPanel()}
                    </div>
                    {renderPacketPanel()}
                  </div>
                  <ResearchCopilotPanel
                    ticker={ticker}
                    companyName={workspace.companyName}
                    session={workspace.copilotSession}
                    targetSection={attachSection}
                    onSessionChange={handleCopilotSessionChange}
                    onDraftNote={handleDraftNote}
                    onDraftMemoSection={handleDraftMemoSection}
                    onAttachCitation={handleAttachCitation}
                    onNotice={setNotice}
                    onError={setError}
                  />
                </div>
              ) : (
                <div className="grid gap-6 xl:grid-cols-[minmax(0,1.25fr)_minmax(22rem,0.95fr)]">
                  <ResearchCopilotPanel
                    ticker={ticker}
                    companyName={workspace.companyName}
                    session={workspace.copilotSession}
                    targetSection={attachSection}
                    variant="focus"
                    onSessionChange={handleCopilotSessionChange}
                    onDraftNote={handleDraftNote}
                    onDraftMemoSection={handleDraftMemoSection}
                    onAttachCitation={handleAttachCitation}
                    onNotice={setNotice}
                    onError={setError}
                  />
                  <div className="space-y-6">
                    <Panel
                      title="Research Surfaces"
                      subtitle="Keep the memo, library, and packet within reach while the copilot stays full-width."
                      actions={<Filter className="size-4 text-[color:var(--accent)]" />}
                    >
                      <div className="flex flex-wrap gap-2">
                        {(['library', 'memo', 'packet'] as const).map((tab) => (
                          <button
                            key={tab}
                            type="button"
                            className={focusTab === tab
                              ? 'rounded-lg border border-[color:var(--line-strong)] bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--terminal-bright)]'
                              : 'rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 text-sm text-[color:var(--terminal-muted)] transition hover:text-[color:var(--terminal-bright)]'
                            }
                            onClick={() => setFocusTab(tab)}
                          >
                            {tab === 'library' ? 'Library' : tab === 'memo' ? 'Memo' : 'Packet'}
                          </button>
                        ))}
                      </div>
                    </Panel>
                    {focusTab === 'library' ? renderLibraryPanel() : null}
                    {focusTab === 'memo' ? renderMemoPanel() : null}
                    {focusTab === 'packet' ? renderPacketPanel() : null}
                  </div>
                </div>
              )}
            </>
          )}
        </>
      ) : null}
    </AppShell>
  );
}

function UploadIcon() {
  return <FolderUp className="size-4" />;
}
