import type { GiteaIssue, GiteaNotification } from './shared';
import { useState } from 'react';
import { Bell, Building2, ExternalLink, MessageSquare, Search } from 'lucide-react';
import { DataRow, RelativeTime } from '@/components/Data';
import { EmptyState, ErrorState, LabelChip, Loading, StateFilter, StateIcon } from './GiteaBits';
import { RepoRow } from './RepositoriesView';
import { giteaRepoPath } from './shared';
import { useGiteaIssueSearch, useGiteaNotifications, useGiteaOrgs, useGiteaRepoSearch } from './useGiteaData';

// The cross-repository sections — the ones Gitea puts in its own header rather than inside a repository:
// your issues, your pull requests, your notifications, explore, and the orgs you belong to.

// ── Issues / pull requests across every repo ──────────────────────────────────────────────────────

type CrossRepoProps = { type: 'issues' | 'pulls' };

export const CrossRepoIssuesView = ({ type }: CrossRepoProps) => {
  const [state, setState] = useState<'open' | 'closed' | 'all'>('open');
  const { data: issues, isLoading, error } = useGiteaIssueSearch({ state, type });
  const noun = type === 'pulls' ? 'pull requests' : 'issues';

  return (
    <div className="flex h-full flex-col">
      <div className="flex items-center gap-3 border-b px-4 py-2">
        <StateFilter value={state} onChange={setState} />
        {!!issues?.length && (
          <span className="text-xs tabular-nums text-muted-foreground">{issues.length} across all repositories</span>
        )}
      </div>
      <div className="min-h-0 flex-1 overflow-y-auto">
        {isLoading ? (
          <Loading label={`Loading ${noun}…`} />
        ) : error ? (
          <ErrorState title={`Could not load ${noun}`} error={error} />
        ) : !issues?.length ? (
          <EmptyState
            title={`No ${state === 'all' ? '' : state} ${noun}`.replace(/\s+/g, ' ').trim()}
            hint={`Gitea searches ${noun} you created, are assigned, or are mentioned in — across every repository this token can see.`}
          />
        ) : (
          <div className="divide-y">
            {issues.map((issue) => (
              <CrossRepoRow key={issue.id} issue={issue} type={type} />
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

/**
 * The cross-repo list is the one place an issue does not already know which repository it is in from the
 * URL, so the row carries `repository` — and links through it, which is why that field is read rather than
 * the issue's own html_url.
 */
const CrossRepoRow = ({ issue, type }: { issue: GiteaIssue; type: 'issues' | 'pulls' }) => {
  const owner = issue.repository?.owner ?? '';
  const name = issue.repository?.name ?? '';
  const to = owner && name ? giteaRepoPath(owner, name, { tab: type, item: issue.number }) : null;

  // Without a repository on the payload there is nothing to route to, so DataRow is left without a `to`
  // and renders a plain div rather than a link that goes somewhere wrong.
  return (
    <DataRow
      to={to ?? undefined}
      lead={<StateIcon item={issue} />}
      title={
        <>
          <span className="truncate text-sm font-medium">{issue.title}</span>
          {issue.labels?.map((label) => (
            <LabelChip key={label.id} label={label} />
          ))}
        </>
      }
      meta={[
        // The repository leads here — it is the fact that makes a cross-repo row make sense, and it is
        // the first thing you need in order to know whether the row is yours to care about.
        issue.repository?.full_name ?? 'unknown',
        `#${issue.number}`,
        <RelativeTime value={issue.created_at} prefix="opened" />,
        issue.user && `by ${issue.user.login}`,
      ]}
      trail={
        !!issue.comments && (
          <span className="flex items-center gap-1.5 text-xs tabular-nums text-muted-foreground">
            <MessageSquare className="h-3.5 w-3.5" />
            {issue.comments}
          </span>
        )
      }
    />
  );
};

// ── Notifications ─────────────────────────────────────────────────────────────────────────────────

/**
 * Turn a notification's API url into an in-app route. Gitea gives the subject as an `/api/v1/...` address
 * because the field is meant for a client to re-fetch, not to navigate; pulling the owner/repo/number back
 * out of it is what lets a notification open in this app instead of bouncing to the instance.
 */
const API_SUBJECT = /\/api\/v1\/repos\/([^/]+)\/([^/]+)\/(issues|pulls)\/(\d+)/;

function subjectRoute(notification: GiteaNotification): string | null {
  const match = notification.subject.url?.match(API_SUBJECT);
  if (!match) return null;
  const [, owner, repo, kind, index] = match;
  if (!owner || !repo || !index) return null;
  // Gitea reports a PR's subject under /issues/, and tells them apart with subject.type instead.
  const tab = notification.subject.type === 'Pull' || kind === 'pulls' ? 'pulls' : 'issues';
  return giteaRepoPath(decodeURIComponent(owner), decodeURIComponent(repo), { tab, item: index });
}

export const NotificationsView = () => {
  const { data: notifications, isLoading, error } = useGiteaNotifications();

  if (isLoading) return <Loading label="Loading notifications…" />;
  if (error) return <ErrorState title="Could not load notifications" error={error} />;
  if (!notifications?.length) {
    return <EmptyState title="Nothing unread" hint="Notifications marked read on the instance disappear here too." />;
  }

  return (
    <div className="h-full overflow-y-auto">
      <div className="divide-y">
        {notifications.map((notification) => {
          // An in-app route when the subject URL can be parsed back into owner/repo/number, and the
          // instance itself when it cannot — the row stays clickable either way, and the external icon
          // is the only thing that tells you which kind of click you are about to make.
          const to = subjectRoute(notification);
          return (
            <DataRow
              key={notification.id}
              to={to ?? undefined}
              href={to ? undefined : notification.subject.html_url}
              lead={<Bell className="h-4 w-4 text-success" />}
              title={notification.subject.title}
              meta={[
                notification.repository?.full_name,
                notification.subject.type,
                <RelativeTime value={notification.updated_at} />,
              ]}
              trail={!to && <ExternalLink className="h-3.5 w-3.5 text-muted-foreground" />}
            />
          );
        })}
      </div>
    </div>
  );
};

// ── Explore ───────────────────────────────────────────────────────────────────────────────────────

export const ExploreView = () => {
  const [input, setInput] = useState('');
  const [query, setQuery] = useState('');
  const { data, isLoading, error } = useGiteaRepoSearch(query);

  return (
    <div className="flex h-full flex-col">
      <form
        className="flex items-center gap-2 border-b px-4 py-2"
        onSubmit={(ev) => {
          ev.preventDefault();
          setQuery(input.trim());
        }}
      >
        <div className="relative flex-1">
          <Search className="absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
          <input
            value={input}
            onChange={(ev) => setInput(ev.target.value)}
            placeholder="Search repositories on this instance…"
            className="w-full rounded-md border bg-background py-1.5 pl-8 pr-2 text-sm outline-none focus:ring-1 focus:ring-primary"
          />
        </div>
        <button type="submit" className="rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted">
          Search
        </button>
      </form>

      <div className="min-h-0 flex-1 overflow-y-auto">
        {isLoading ? (
          <Loading label="Searching…" />
        ) : error ? (
          <ErrorState title="Search failed" error={error} />
        ) : !data?.data?.length ? (
          <EmptyState
            title={query ? `Nothing matches “${query}”` : 'No repositories'}
            hint={
              query
                ? 'Search covers repository names and descriptions on this instance, limited to what your token can see.'
                : 'Search this instance by repository name or description.'
            }
          />
        ) : (
          <div className="divide-y">
            {data.data.map((repo) => (
              <RepoRow key={repo.id} repo={repo} />
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

// ── Organizations ─────────────────────────────────────────────────────────────────────────────────

export const OrganizationsView = () => {
  const { data: orgs, isLoading, error } = useGiteaOrgs();

  if (isLoading) return <Loading label="Loading organizations…" />;
  if (error) return <ErrorState title="Could not load organizations" error={error} />;
  if (!orgs?.length) return <EmptyState title="No organizations" hint="This account does not belong to any." />;

  return (
    <div className="h-full overflow-y-auto">
      <div className="divide-y">
        {orgs.map((org) => (
          <DataRow
            key={org.id}
            lead={
              org.avatar_url ? (
                <img src={org.avatar_url} alt="" className="h-8 w-8 rounded-full object-cover" loading="lazy" />
              ) : (
                <span className="flex h-8 w-8 items-center justify-center rounded-full bg-muted">
                  <Building2 className="h-4 w-4 text-muted-foreground" />
                </span>
              )
            }
            title={org.full_name || org.username}
            description={org.description}
            meta={[`@${org.username}`, org.visibility, org.location]}
          />
        ))}
      </div>
    </div>
  );
};
