import { useCoreServices } from '@learnitive/context';
import type { ReviewActivityKind } from '@learnitive/service-core';
import { Button, toast } from '@learnitive/ui-components';
import { Camera, GitCommitHorizontal, History, RefreshCcw } from 'lucide-react';
import React from 'react';

type SinceMode = 'all' | 'yesterday' | 'git-head' | 'snapshot' | 'commit' | 'date';
type KindGroup = 'all' | 'comments' | 'changes' | 'ai' | 'snapshots';

const COMMENT_KINDS: readonly ReviewActivityKind[] = [
  'comment-created',
  'comment-replied',
  'comment-edited',
  'comment-deleted',
  'comment-resolved',
  'comment-reopened',
];
const CHANGE_KINDS: readonly ReviewActivityKind[] = [
  'change-proposed',
  'change-accepted',
  'change-rejected',
  'change-outdated',
];

function kindsFor(group: KindGroup): readonly ReviewActivityKind[] | undefined {
  if (group === 'comments') return COMMENT_KINDS;
  if (group === 'changes') return CHANGE_KINDS;
  if (group === 'snapshots') return ['snapshot-created'];
  return undefined;
}

function labelForKind(kind: ReviewActivityKind): string {
  return kind.split('-').map((part) => part[0]!.toUpperCase() + part.slice(1)).join(' ');
}

export function ReviewHistoryPanel({
  wsName,
  currentFile,
  onOpenThread,
}: {
  wsName: string;
  currentFile?: string;
  onOpenThread: (id: string) => void;
}) {
  const { projectReview, projectGit, noteSnapshot } = useCoreServices();
  const [kindGroup, setKindGroup] = React.useState<KindGroup>('all');
  const [authorId, setAuthorId] = React.useState('all');
  const [changeType, setChangeType] = React.useState<'all' | 'insert' | 'delete' | 'replace'>('all');
  const [filePath, setFilePath] = React.useState('all');
  const [sinceMode, setSinceMode] = React.useState<SinceMode>('all');
  const [selectedCommit, setSelectedCommit] = React.useState('');
  const [customDate, setCustomDate] = React.useState('');
  const [commits, setCommits] = React.useState<readonly { oid: string; message: string; timestamp: number }[]>([]);
  const [snapshots, setSnapshots] = React.useState<readonly { wsPath: string; createdAt: number }[]>([]);
  const [gitSummary, setGitSummary] = React.useState<string>();
  const [loadingContext, setLoadingContext] = React.useState(false);

  const loadContext = React.useCallback(async () => {
    setLoadingContext(true);
    try {
      const nextSnapshots = await noteSnapshot.listSnapshots({ wsName });
      setSnapshots(nextSnapshots);
      if (await projectGit.isRepository(wsName)) {
        const [history, status] = await Promise.all([projectGit.log(wsName, 50), projectGit.status(wsName)]);
        setCommits(history);
        const changed = status.staged.length + status.unstaged.length;
        setGitSummary(`${status.branch ?? 'detached'} · ${changed} working-tree change${changed === 1 ? '' : 's'}`);
      } else {
        setCommits([]);
        setGitSummary('Not a Git repository');
      }
    } catch (reason) {
      setCommits([]);
      setGitSummary('Git history unavailable');
      toast.error(reason instanceof Error ? reason.message : 'Unable to load review history context.');
    } finally {
      setLoadingContext(false);
    }
  }, [noteSnapshot, projectGit, wsName]);

  React.useEffect(() => {
    void loadContext();
  }, [loadContext]);

  const allActivity = projectReview.getActivity(wsName);
  const authors = projectReview.getActivityAuthors(wsName);
  const files = React.useMemo(
    () => [...new Set(allActivity.map((item) => item.filePath).filter((value): value is string => Boolean(value)))].sort(),
    [allActivity],
  );

  const cutoff = React.useMemo(() => {
    if (sinceMode === 'all') return undefined;
    if (sinceMode === 'yesterday') {
      const value = new Date();
      value.setDate(value.getDate() - 1);
      value.setHours(0, 0, 0, 0);
      return value.getTime();
    }
    if (sinceMode === 'git-head') return commits[0] ? commits[0].timestamp * 1000 : undefined;
    if (sinceMode === 'snapshot') {
      const relevant = filePath === 'all'
        ? snapshots
        : snapshots.filter((item) => item.wsPath.endsWith(`:${filePath}`));
      return [...relevant].sort((a, b) => b.createdAt - a.createdAt)[0]?.createdAt;
    }
    if (sinceMode === 'commit') return commits.find((commit) => commit.oid === selectedCommit)?.timestamp ? commits.find((commit) => commit.oid === selectedCommit)!.timestamp * 1000 : undefined;
    if (sinceMode === 'date') {
      const parsed = customDate ? new Date(customDate).getTime() : Number.NaN;
      return Number.isFinite(parsed) ? parsed : undefined;
    }
    return undefined;
  }, [commits, customDate, filePath, selectedCommit, sinceMode, snapshots]);

  const activity = projectReview.getActivity(wsName, {
    ...(filePath !== 'all' ? { file: filePath } : {}),
    ...(authorId !== 'all' ? { authorId } : {}),
    ...(kindsFor(kindGroup) ? { kinds: kindsFor(kindGroup)! } : {}),
    ...(cutoff !== undefined ? { since: cutoff } : {}),
  })
    .filter((item) => kindGroup !== 'ai' || item.source === 'ai' || item.author.kind === 'ai')
    .filter((item) => changeType === 'all' || item.changeType === changeType);

  const createSnapshot = async () => {
    if (!currentFile) return;
    try {
      await projectReview.createManualSnapshot(wsName, currentFile);
      toast.success('Review snapshot created');
      await loadContext();
    } catch (reason) {
      toast.error(reason instanceof Error ? reason.message : 'Unable to create review snapshot.');
    }
  };

  return (
    <div className="flex min-h-0 flex-1 flex-col" data-testid="review-history-panel">
      <div className="space-y-2 border-b p-2 text-xs">
        <div className="flex items-center gap-1">
          <History className="size-3.5" />
          <b>Review activity</b>
          <Button className="ml-auto" size="icon" variant="ghost" aria-label="Refresh review history" onClick={() => void loadContext()} disabled={loadingContext}><RefreshCcw className="size-3.5" /></Button>
        </div>
        <div className="grid grid-cols-2 gap-2">
          <label className="grid gap-1">Type
            <select className="rounded border bg-background px-2 py-1" value={kindGroup} onChange={(event) => setKindGroup(event.currentTarget.value as KindGroup)}>
              <option value="all">All activity</option><option value="comments">Comments</option><option value="changes">Changes</option><option value="ai">AI suggestions</option><option value="snapshots">Snapshots</option>
            </select>
          </label>
          <label className="grid gap-1">Author
            <select className="rounded border bg-background px-2 py-1" value={authorId} onChange={(event) => setAuthorId(event.currentTarget.value)}>
              <option value="all">All authors</option>{authors.map((author) => <option key={author.id} value={author.id}>{author.displayName}</option>)}
            </select>
          </label>
          <label className="grid gap-1">Change type
            <select className="rounded border bg-background px-2 py-1" value={changeType} onChange={(event) => setChangeType(event.currentTarget.value as typeof changeType)}>
              <option value="all">Any change</option><option value="insert">Insert</option><option value="delete">Delete</option><option value="replace">Replace</option>
            </select>
          </label>
          <label className="grid gap-1">File
            <select className="rounded border bg-background px-2 py-1" value={filePath} onChange={(event) => setFilePath(event.currentTarget.value)}>
              <option value="all">Whole project</option>{currentFile && !files.includes(currentFile) && <option value={currentFile}>{currentFile}</option>}{files.map((file) => <option key={file} value={file}>{file}</option>)}
            </select>
          </label>
          <label className="grid gap-1">Changes since
            <select className="rounded border bg-background px-2 py-1" value={sinceMode} onChange={(event) => setSinceMode(event.currentTarget.value as SinceMode)}>
              <option value="all">All history</option><option value="yesterday">Yesterday</option><option value="git-head">Last Git commit</option><option value="snapshot">Last local snapshot</option><option value="commit">Selected Git commit</option><option value="date">Selected date</option>
            </select>
          </label>
        </div>
        {sinceMode === 'commit' && <select className="w-full rounded border bg-background px-2 py-1" aria-label="Selected Git commit" value={selectedCommit} onChange={(event) => setSelectedCommit(event.currentTarget.value)}><option value="">Choose commit…</option>{commits.map((commit) => <option key={commit.oid} value={commit.oid}>{commit.oid.slice(0, 7)} · {commit.message.trim().slice(0, 70)}</option>)}</select>}
        {sinceMode === 'date' && <input type="datetime-local" className="w-full rounded border bg-background px-2 py-1" aria-label="Review history start date" value={customDate} onChange={(event) => setCustomDate(event.currentTarget.value)} />}
        <div className="flex flex-wrap items-center gap-1 text-[11px] text-muted-foreground">
          <GitCommitHorizontal className="size-3" /><span>{gitSummary ?? 'Loading Git context…'}</span>
          <span>·</span><span>{snapshots.length} local snapshot{snapshots.length === 1 ? '' : 's'}</span>
        </div>
        <Button size="sm" variant="outline" disabled={!currentFile} onClick={() => void createSnapshot()}><Camera className="mr-1 size-3.5" />Snapshot current file</Button>
      </div>
      <div className="min-h-0 flex-1 overflow-auto p-2">
        {activity.length === 0 ? <p className="p-3 text-sm text-muted-foreground">No review activity matches these filters.</p> : activity.map((item) => (
          <article key={item.id} className="mb-2 rounded border bg-card p-2 text-xs" data-testid={`review-activity-${item.kind}`}>
            <button type="button" className="w-full text-left disabled:cursor-default" disabled={!item.threadId} onClick={() => item.threadId && onOpenThread(item.threadId)}>
              <div className="flex flex-wrap items-center gap-1 text-muted-foreground">
                <b className="text-foreground">{item.author.displayName}</b>
                {item.source === 'ai' && <span className="rounded bg-muted px-1">AI</span>}
                {item.source === 'system' && <span className="rounded bg-muted px-1">System</span>}
                <span className="rounded bg-muted px-1">{labelForKind(item.kind)}</span>
                <span>·</span><span>{new Date(item.createdAt).toLocaleString()}</span>
              </div>
              <p className="mt-1 text-foreground">{item.summary}</p>
              {item.filePath && <p className="mt-0.5 truncate text-[11px] text-muted-foreground">{item.filePath}{item.changeType ? ` · ${item.changeType}` : ''}</p>}
            </button>
          </article>
        ))}
      </div>
    </div>
  );
}
