import type { Tone } from '@/components/Data';
import type { GiteaChangedFile, GiteaRepo } from './shared';
import { Link } from 'react-router';
import { ArrowLeft, ArrowRight, ExternalLink, FileDiff } from 'lucide-react';
import { RelativeTime, toneText } from '@/components/Data';
import { EmptyState, ErrorState, LabelChip, Loading, StateBadge } from './GiteaBits';
import { CommentCard, RepoIssuesView } from './RepoIssuesView';
import { giteaRepoPath, shortSha } from './shared';
import { useGiteaIssueComments, useGiteaPull, useGiteaPullFiles } from './useGiteaData';

// Pull requests for one repository.
//
// The LIST is `RepoIssuesView` with type='pulls' — in Gitea a PR is an issue, so the same endpoint and the
// same row layout serve both and there is nothing to duplicate. The DETAIL is its own screen, because a PR
// carries things an issue does not: the branch pair, the merge state and the changed-file summary.

type RepoPullsViewProps = { repo: GiteaRepo; owner: string; name: string; item: string | null };

export const RepoPullsView = ({ repo, owner, name, item }: RepoPullsViewProps) => {
  const index = item ? Number(item) : null;
  if (index == null || !Number.isFinite(index)) {
    return <RepoIssuesView repo={repo} owner={owner} name={name} item={null} type="pulls" />;
  }
  return <PullDetail repo={repo} owner={owner} name={name} index={index} />;
};

type DetailProps = { repo: GiteaRepo; owner: string; name: string; index: number };

const PullDetail = ({ repo, owner, name, index }: DetailProps) => {
  const { data: pull, isLoading, error } = useGiteaPull({ owner, repo: name }, index);
  const { data: comments } = useGiteaIssueComments({ owner, repo: name }, index);
  const { data: files } = useGiteaPullFiles({ owner, repo: name }, index);

  if (isLoading) return <Loading label={`Loading !${index}…`} />;
  if (error) return <ErrorState title={`Could not load pull request #${index}`} error={error} />;
  if (!pull) return <EmptyState title={`#${index} not found`} />;

  return (
    <div className="h-full overflow-y-auto">
      <div className="border-b px-4 py-3">
        <Link
          to={giteaRepoPath(owner, name, { tab: 'pulls' })}
          className="mb-2 inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground"
        >
          <ArrowLeft className="h-3 w-3" />
          Back to pull requests
        </Link>
        <div className="flex items-start gap-2">
          <h2 className="min-w-0 flex-1 text-base font-semibold leading-snug">
            {pull.title} <span className="font-normal text-muted-foreground">#{pull.number}</span>
          </h2>
          <a
            href={pull.html_url}
            target="_blank"
            rel="noreferrer"
            className="shrink-0 text-muted-foreground hover:text-foreground"
            title="Open in Gitea"
          >
            <ExternalLink className="h-4 w-4" />
          </a>
        </div>

        <div className="mt-2 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
          <StateBadge item={pull} />
          {pull.draft && <span className="rounded-full bg-muted px-2 py-0.5 font-medium">Draft</span>}
          <span>
            {pull.user?.login ?? 'someone'} wants to merge <RelativeTime value={pull.created_at} />
          </span>
          {pull.labels?.map((label) => (
            <LabelChip key={label.id} label={label} />
          ))}
        </div>

        <div className="mt-2 flex flex-wrap items-center gap-1.5 text-xs">
          <code className="rounded bg-muted px-1.5 py-0.5 font-mono">{pull.head?.label ?? '?'}</code>
          <ArrowRight className="h-3 w-3 text-muted-foreground" />
          <code className="rounded bg-muted px-1.5 py-0.5 font-mono">{pull.base?.label ?? '?'}</code>
          {pull.merge_base && <span className="text-muted-foreground">· base {shortSha(pull.merge_base)}</span>}
        </div>

        <div className="mt-2 flex flex-wrap items-center gap-3 text-xs tabular-nums text-muted-foreground">
          {pull.changed_files != null && (
            <span className="flex items-center gap-1">
              <FileDiff className="h-3 w-3" />
              {pull.changed_files} files
            </span>
          )}
          {pull.additions != null && <span className="text-success">+{pull.additions}</span>}
          {pull.deletions != null && <span className="text-destructive">−{pull.deletions}</span>}
          {/* `mergeable` is only meaningful while the PR is still open — Gitea leaves it false once merged. */}
          {pull.state === 'open' && !pull.merged && (
            <span className={pull.mergeable ? 'text-success' : 'text-warning'}>
              {pull.mergeable ? 'No conflicts' : 'Conflicts'}
            </span>
          )}
        </div>
      </div>

      <div className="flex flex-col gap-3 px-4 pb-6 pt-3">
        <CommentCard
          author={pull.user}
          createdAt={pull.created_at}
          body={pull.body}
          repo={repo}
          emptyText="No description provided."
        />

        {!!files?.length && <ChangedFiles files={files} owner={owner} name={name} headRef={pull.head?.ref} />}

        {comments?.map((comment) => (
          <CommentCard
            key={comment.id}
            author={comment.user}
            createdAt={comment.created_at}
            body={comment.body}
            repo={repo}
          />
        ))}
      </div>
    </div>
  );
};

// ── Changed files ─────────────────────────────────────────────────────────────────────────────────

/** Tones, not palette numbers — added is good news, removed is destructive, renamed is neither. */
const STATUS_TONE: Record<string, Tone> = {
  added: 'success',
  modified: 'warning',
  removed: 'danger',
  renamed: 'info',
};

type ChangedFilesProps = { files: GiteaChangedFile[]; owner: string; name: string; headRef?: string };

/**
 * The file summary, not the patch. Gitea's `/pulls/{index}/files` returns per-file +/- counts and links but
 * no diff text — the patch is only available as a whole-PR `.diff` from a non-API URL — so each row links
 * into the code browser at the head ref instead of pretending to show a hunk.
 */
const ChangedFiles = ({ files, owner, name, headRef }: ChangedFilesProps) => (
  <div className="overflow-hidden rounded-lg border">
    <div className="flex items-center gap-2 border-b bg-muted/40 px-3 py-2 text-xs font-medium">
      <FileDiff className="h-3.5 w-3.5 text-muted-foreground" />
      {files.length} changed {files.length === 1 ? 'file' : 'files'}
    </div>
    <div className="divide-y">
      {files.map((file) => (
        <Link
          key={file.filename}
          to={giteaRepoPath(owner, name, { tab: 'code', path: file.filename, ref: headRef })}
          className="flex items-center gap-3 px-3 py-2 text-sm transition-colors hover:bg-muted/50"
        >
          <span
            className={`w-16 shrink-0 text-xs font-medium capitalize ${toneText[STATUS_TONE[file.status] ?? 'neutral']}`}
          >
            {file.status}
          </span>
          <span className="min-w-0 flex-1 truncate font-mono">{file.filename}</span>
          <span className="shrink-0 text-xs tabular-nums text-success">+{file.additions}</span>
          <span className="shrink-0 text-xs tabular-nums text-destructive">−{file.deletions}</span>
        </Link>
      ))}
    </div>
  </div>
);
