import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { ChevronLeft, ChevronRight, ExternalLink, FileSpreadsheet, LoaderCircle, Search, Unplug } from 'lucide-react';
import { ApiError, api, get, post, relativeTime } from '@/lib/api';
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, EmptyState, Input, Skeleton } from '@/components/ui';
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';

export interface GoogleParsedTable {
  fileName: string;
  sheetName: string | null;
  headers: string[];
  rows: string[][];
  warnings: string[];
}

interface ConnectionStatus {
  configured: boolean;
  connected: boolean;
  connectedAt: string | null;
  scopes: string[];
}

interface DriveFile {
  id: string;
  name: string;
  modifiedTime: string | null;
}

interface FilePage {
  files: DriveFile[];
  nextPageToken: string | null;
  incomplete: boolean;
}

interface SpreadsheetMetadata {
  title: string;
  sheets: { sheetId: number; title: string; rowCount: number; columnCount: number }[];
}

export function GoogleSheetsSource({ onLoaded }: { onLoaded(table: GoogleParsedTable): void }) {
  const queryClient = useQueryClient();
  const [searchDraft, setSearchDraft] = useState('');
  const [search, setSearch] = useState('');
  const [pageToken, setPageToken] = useState<string | null>(null);
  const [previousTokens, setPreviousTokens] = useState<(string | null)[]>([]);
  const [spreadsheetId, setSpreadsheetId] = useState('');
  const [sheetId, setSheetId] = useState('');
  const [range, setRange] = useState('A1:Z2001');
  const { data: status, isLoading: statusLoading } = useQuery({
    queryKey: ['google-sheets', 'status'],
    queryFn: () => get<ConnectionStatus>('/api/imports/google/status'),
  });
  const connect = useMutation({
    mutationFn: () => post<{ authorizationUrl: string }>('/api/imports/google/connect', {}),
    onSuccess: ({ authorizationUrl }) => window.location.assign(authorizationUrl),
  });
  const disconnect = useMutation({
    mutationFn: () => api<void>('/api/imports/google/connection', { method: 'DELETE' }),
    onSuccess: async () => {
      setSpreadsheetId('');
      setSheetId('');
      await queryClient.invalidateQueries({ queryKey: ['google-sheets'] });
    },
  });
  const files = useQuery({
    queryKey: ['google-sheets', 'files', search, pageToken],
    queryFn: () => {
      const parameters = new URLSearchParams();
      if (search) parameters.set('search', search);
      if (pageToken) parameters.set('pageToken', pageToken);
      return get<FilePage>(`/api/imports/google/files?${parameters}`);
    },
    enabled: status?.connected === true,
  });
  const metadata = useQuery({
    queryKey: ['google-sheets', 'metadata', spreadsheetId],
    queryFn: () => get<SpreadsheetMetadata>(`/api/imports/google/spreadsheets/${encodeURIComponent(spreadsheetId)}/sheets`),
    enabled: Boolean(spreadsheetId),
  });
  const load = useMutation({
    mutationFn: () => post<GoogleParsedTable>('/api/imports/google/table', {
      spreadsheetId,
      sheetId: Number(sheetId),
      range,
    }),
    onSuccess: onLoaded,
  });

  if (statusLoading) return <Skeleton className="h-44" />;
  if (!status?.configured) {
    return <Card><EmptyState icon={<FileSpreadsheet className="size-8" />} title="Google Sheets is not configured" description="An operator must configure the Google OAuth client and PIG settings encryption key before connecting." /></Card>;
  }
  if (!status.connected) {
    return (
      <Card>
        <CardHeader><CardTitle className="text-base">Connect Google Sheets</CardTitle></CardHeader>
        <CardContent className="flex flex-col gap-3">
          <p className="text-sm text-muted">PIG requests read-only spreadsheet values and Drive metadata only when you start an import. Tokens remain encrypted on the server and are never returned to this page.</p>
          <Button className="w-full sm:w-auto" variant="primary" disabled={connect.isPending} onClick={() => connect.mutate()}>
            {connect.isPending ? <LoaderCircle data-icon="inline-start" className="animate-spin" aria-hidden /> : <ExternalLink data-icon="inline-start" aria-hidden />}
            Connect Google
          </Button>
          {connect.isError ? <ErrorText error={connect.error} /> : null}
        </CardContent>
      </Card>
    );
  }

  const selectedSheet = metadata.data?.sheets.find((sheet) => String(sheet.sheetId) === sheetId);
  return (
    <div className="flex flex-col gap-4">
      <div className="flex flex-col gap-3 rounded-lg border border-border bg-surface-2 p-4 sm:flex-row sm:items-center sm:justify-between">
        <div><div className="mb-1 flex items-center gap-2"><p className="text-sm font-medium">Google Sheets connected</p><Badge tone="neutral">Read only</Badge></div><p className="text-xs text-muted">{status.connectedAt ? `Connected ${relativeTime(status.connectedAt)}` : 'Encrypted server-side connection'} · selecting a range only stages a preview</p></div>
        <Button variant="outline" disabled={disconnect.isPending} onClick={() => disconnect.mutate()}><Unplug data-icon="inline-start" aria-hidden />Disconnect</Button>
      </div>
      {disconnect.isError ? <ErrorText error={disconnect.error} /> : null}

      <Card>
        <CardHeader><CardTitle className="text-base">Choose a spreadsheet</CardTitle></CardHeader>
        <CardContent className="flex flex-col gap-4">
          <form className="flex gap-2" onSubmit={(event) => {
            event.preventDefault();
            setSearch(searchDraft.trim());
            setPageToken(null);
            setPreviousTokens([]);
          }}>
            <Input name="spreadsheetSearch" aria-label="Search spreadsheet names" value={searchDraft} onChange={(event) => setSearchDraft(event.target.value)} placeholder="Search spreadsheet names" />
            <Button type="submit" variant="outline"><Search data-icon="inline-start" aria-hidden />Search</Button>
          </form>
          {files.isLoading ? <Skeleton className="h-40" /> : files.isError ? <ErrorText error={files.error} /> : files.data?.files.length === 0 ? <EmptyState title="No spreadsheets found" description="Try another name or confirm this Google account can see the spreadsheet." /> : (
            <div className="grid gap-2 sm:grid-cols-2">
              {files.data?.files.map((file) => (
                <button key={file.id} type="button" aria-pressed={spreadsheetId === file.id} onClick={() => { setSpreadsheetId(file.id); setSheetId(''); }} className={spreadsheetId === file.id ? 'tap min-w-0 rounded-lg border border-primary bg-accent-subtle p-3 text-left' : 'tap min-w-0 rounded-lg border border-border p-3 text-left hover:bg-surface-2'}>
                  <p className="truncate text-sm font-medium">{file.name}</p>
                  <p className="mt-1 text-xs text-muted">{file.modifiedTime ? `Modified ${relativeTime(file.modifiedTime)}` : 'Modified time unavailable'}</p>
                </button>
              ))}
            </div>
          )}
          {files.data?.incomplete ? <p role="alert" className="text-xs text-warning">Google reported an incomplete Drive search. Narrow the spreadsheet name and search again.</p> : null}
          <div className="flex items-center justify-between">
            <Button variant="outline" disabled={previousTokens.length === 0} onClick={() => {
              setPreviousTokens((tokens) => {
                const next = [...tokens];
                setPageToken(next.pop() ?? null);
                return next;
              });
            }}><ChevronLeft data-icon="inline-start" aria-hidden />Previous</Button>
            <Badge tone="neutral">Up to 50 per page</Badge>
            <Button variant="outline" disabled={!files.data?.nextPageToken} onClick={() => {
              if (!files.data?.nextPageToken) return;
              setPreviousTokens((tokens) => [...tokens, pageToken]);
              setPageToken(files.data.nextPageToken);
            }}>Next<ChevronRight data-icon="inline-end" aria-hidden /></Button>
          </div>
        </CardContent>
      </Card>

      {spreadsheetId ? (
        <Card>
          <CardHeader><CardTitle className="text-base">Choose a sheet and bounded range</CardTitle><p className="text-xs text-muted">A rectangular A1 range must include one header row and at most 2,000 data rows by 100 columns.</p></CardHeader>
          <CardContent className="grid grid-cols-1 gap-4 sm:grid-cols-2">
            <label className="flex flex-col gap-1.5 text-sm font-medium">Sheet
              <Select value={sheetId} onValueChange={setSheetId} disabled={metadata.isLoading}>
                <SelectTrigger className="h-11"><SelectValue placeholder={metadata.isLoading ? 'Loading sheets…' : 'Select visible sheet'} /></SelectTrigger>
                <SelectContent><SelectGroup>{metadata.data?.sheets.map((sheet) => <SelectItem key={sheet.sheetId} value={String(sheet.sheetId)}>{sheet.title} · {sheet.rowCount}×{sheet.columnCount}</SelectItem>)}</SelectGroup></SelectContent>
              </Select>
            </label>
            <label className="flex flex-col gap-1.5 text-sm font-medium">A1 range
              <Input name="a1Range" value={range} onChange={(event) => setRange(event.target.value)} placeholder="A1:H500" autoCapitalize="off" autoCorrect="off" spellCheck={false} />
            </label>
            {metadata.isError ? <div className="sm:col-span-2"><ErrorText error={metadata.error} /></div> : null}
            {selectedSheet ? <p className="text-xs text-muted sm:col-span-2">Selected grid: {selectedSheet.rowCount} rows × {selectedSheet.columnCount} columns. Range limits are enforced again by the server.</p> : null}
            <Button className="sm:col-span-2" variant="primary" disabled={!sheetId || !range.trim() || load.isPending} onClick={() => load.mutate()}>
              {load.isPending ? <LoaderCircle data-icon="inline-start" className="animate-spin" aria-hidden /> : <FileSpreadsheet data-icon="inline-start" aria-hidden />}
              {load.isPending ? 'Reading bounded cells…' : 'Use this range'}
            </Button>
            {load.isError ? <div className="sm:col-span-2"><ErrorText error={load.error} /></div> : null}
          </CardContent>
        </Card>
      ) : null}
    </div>
  );
}

function ErrorText({ error }: { error: unknown }) {
  return <p role="alert" className="text-sm text-danger">{error instanceof ApiError || error instanceof Error ? error.message : 'Google Sheets request failed.'}</p>;
}
