import { useState } from 'react';
import { Alert, Text, View } from 'react-native';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { useAction, useMutation, useQuery } from 'convex/react';

import type { Id } from '@spoon/backend/convex/_generated/dataModel.js';
import { api } from '@spoon/backend/convex/_generated/api.js';

import type { SpoonDetailSegment } from '~/components/spoons/segment-control';
import { SegmentControl } from '~/components/spoons/segment-control';
import { SpoonDetailFork } from '~/components/spoons/spoon-detail-fork';
import { SpoonDetailOverview } from '~/components/spoons/spoon-detail-overview';
import { SpoonDetailPrs } from '~/components/spoons/spoon-detail-prs';
import { SpoonDetailSettings } from '~/components/spoons/spoon-detail-settings';
import { SpoonDetailThreads } from '~/components/spoons/spoon-detail-threads';
import { SpoonDetailUpstream } from '~/components/spoons/spoon-detail-upstream';
import { SpoonStatusBadge } from '~/components/spoons/spoon-status-badge';
import { AppScreen } from '~/components/ui/app-screen';
import { Button } from '~/components/ui/button';

const SpoonDetailRoute = () => {
  const router = useRouter();
  const { spoonId: rawSpoonId } = useLocalSearchParams<{ spoonId: string }>();
  const spoonId = rawSpoonId as Id<'spoons'>;
  const [segment, setSegment] = useState<SpoonDetailSegment>('overview');
  const [threadPrompt, setThreadPrompt] = useState('');
  const [pending, setPending] = useState<string | undefined>();
  const [refreshing, setRefreshing] = useState(false);

  const details = useQuery(api.spoons.getDetails, { spoonId });
  const upstreamCommits =
    useQuery(api.spoonCommits.listForSpoon, {
      limit: 50,
      side: 'upstream',
      spoonId,
    }) ?? [];
  const forkCommits =
    useQuery(api.spoonCommits.listForSpoon, {
      limit: 50,
      side: 'fork',
      spoonId,
    }) ?? [];
  const pullRequests =
    useQuery(api.spoonPullRequests.listForSpoon, { limit: 50, spoonId }) ?? [];
  const remotes = useQuery(api.spoonRemotes.listForSpoon, { spoonId }) ?? [];
  const threads =
    useQuery(api.threads.listForSpoon, { limit: 25, spoonId }) ?? [];
  const spoonSettings = useQuery(api.spoonSettings.getForSpoon, { spoonId });
  const agentSettings = useQuery(api.spoonAgentSettings.getForSpoon, {
    spoonId,
  });
  const secrets = useQuery(api.spoonSecrets.listForSpoon, { spoonId }) ?? [];
  const providerProfiles = useQuery(api.aiProviderProfiles.listMine, {}) ?? [];

  const refresh = useAction(api.githubSync.refreshSpoonGithubState);
  const sync = useAction(api.githubSync.syncForkWithUpstream);
  const updateSpoonSettings = useMutation(api.spoons.updateSettings);
  const updateMaintenanceSettings = useMutation(api.spoonSettings.update);
  const updateAgentSettings = useMutation(api.spoonAgentSettings.update);
  const createThread = useMutation(api.threads.createUserThread);
  const createSecret = useAction(api.spoonSecretsNode.create);
  const removeSecretMutation = useMutation(api.spoonSecrets.remove);
  const createRemote = useMutation(api.spoonRemotes.create);
  const removeRemoteMutation = useMutation(api.spoonRemotes.remove);

  const runRefresh = async () => {
    setRefreshing(true);
    setPending('refresh');
    try {
      await refresh({ spoonId });
      Alert.alert('Refresh started', 'Spoon is checking GitHub state.');
    } catch (error) {
      console.error(error);
      Alert.alert('Could not refresh this Spoon.');
    } finally {
      setRefreshing(false);
      setPending(undefined);
    }
  };

  const runSync = async () => {
    setPending('sync');
    try {
      await sync({ spoonId });
      Alert.alert('Sync started', 'Spoon is syncing the fork.');
    } catch (error) {
      console.error(error);
      Alert.alert('Could not sync this Spoon.');
    } finally {
      setPending(undefined);
    }
  };

  const submitThread = async () => {
    if (!threadPrompt.trim()) return;
    setPending('thread');
    try {
      const threadId = await createThread({
        envFilePath:
          agentSettings?.envFilePath === 'custom'
            ? agentSettings.customEnvFilePath
            : agentSettings?.envFilePath,
        materializeEnvFile: agentSettings?.materializeEnvFileByDefault,
        prompt: threadPrompt,
        spoonId,
      });
      setThreadPrompt('');
      router.push(`/threads/${threadId}`);
    } catch (error) {
      console.error(error);
      Alert.alert('Could not create thread.');
    } finally {
      setPending(undefined);
    }
  };

  if (!details) {
    return (
      <AppScreen>
        <Stack.Screen options={{ title: 'Spoon' }} />
        <Text className='text-muted-foreground'>Loading Spoon...</Text>
      </AppScreen>
    );
  }

  const { effectiveUpstreamAheadBy, spoon } = details;
  const canSync =
    spoon.provider === 'github' &&
    (spoon.syncStatus === 'behind' || spoon.syncStatus === 'up_to_date') &&
    (spoon.forkAheadBy ?? 0) === 0;

  const settingsActions = {
    addRemote: async (label: string, url: string) => {
      setPending('addRemote');
      try {
        await createRemote({ label, spoonId, url });
      } finally {
        setPending(undefined);
      }
    },
    addSecret: async (name: string, value: string) => {
      setPending('addSecret');
      try {
        await createSecret({ name, spoonId, value });
      } finally {
        setPending(undefined);
      }
    },
    importSecrets: async (items: { name: string; value: string }[]) => {
      setPending('importSecrets');
      let failed = 0;
      try {
        for (const item of items) {
          try {
            await createSecret({ name: item.name, spoonId, value: item.value });
          } catch (error) {
            failed += 1;
            console.error(error);
          }
        }
        if (failed > 0) {
          throw new Error(
            `${items.length - failed} imported, ${failed} failed.`,
          );
        }
      } finally {
        setPending(undefined);
      }
    },
    removeRemote: async (remoteId: string) => {
      setPending(`remote:${remoteId}`);
      try {
        await removeRemoteMutation({
          remoteId: remoteId as Id<'spoonRemotes'>,
        });
      } finally {
        setPending(undefined);
      }
    },
    removeSecret: async (secretId: string) => {
      setPending(`secret:${secretId}`);
      try {
        await removeSecretMutation({
          secretId: secretId as Id<'spoonSecrets'>,
        });
      } finally {
        setPending(undefined);
      }
    },
    updateAgent: async (patch: Record<string, unknown>) => {
      setPending('settings');
      try {
        await updateAgentSettings({ spoonId, ...patch });
      } finally {
        setPending(undefined);
      }
    },
    updateMaintenance: async (patch: Record<string, unknown>) => {
      setPending('settings');
      try {
        await updateMaintenanceSettings({ spoonId, ...patch });
      } finally {
        setPending(undefined);
      }
    },
    updateSpoon: async (patch: Record<string, unknown>) => {
      setPending('settings');
      try {
        await updateSpoonSettings({ spoonId, ...patch });
      } finally {
        setPending(undefined);
      }
    },
  };

  return (
    <AppScreen onRefresh={() => void runRefresh()} refreshing={refreshing}>
      <Stack.Screen options={{ title: spoon.name }} />
      <View className='gap-2'>
        <Text className='text-foreground text-3xl font-bold'>{spoon.name}</Text>
        <View className='flex-row flex-wrap gap-2'>
          <SpoonStatusBadge status={spoon.syncStatus ?? spoon.status} />
          <SpoonStatusBadge status={spoon.status} />
        </View>
      </View>

      <View className='flex-row gap-3'>
        <Button
          disabled={pending === 'refresh'}
          onPress={() => void runRefresh()}
        >
          {pending === 'refresh' ? 'Refreshing...' : 'Refresh'}
        </Button>
        <Button
          disabled={!canSync || pending === 'sync'}
          variant='outline'
          onPress={() => void runSync()}
        >
          {pending === 'sync' ? 'Syncing...' : 'Sync fork'}
        </Button>
      </View>

      <SegmentControl value={segment} onChange={setSegment} />

      {segment === 'overview' ? (
        <SpoonDetailOverview
          effectiveUpstreamAheadBy={effectiveUpstreamAheadBy}
          remotes={remotes}
          spoon={spoon}
        />
      ) : null}
      {segment === 'upstream' ? (
        <SpoonDetailUpstream commits={upstreamCommits} />
      ) : null}
      {segment === 'fork' ? <SpoonDetailFork commits={forkCommits} /> : null}
      {segment === 'prs' ? (
        <SpoonDetailPrs pullRequests={pullRequests} />
      ) : null}
      {segment === 'threads' ? (
        <SpoonDetailThreads
          creating={pending === 'thread'}
          prompt={threadPrompt}
          setPrompt={setThreadPrompt}
          threads={threads}
          onCreate={() => void submitThread()}
          onOpenThread={(threadId) => router.push(`/threads/${threadId}`)}
        />
      ) : null}
      {segment === 'settings' ? (
        <SpoonDetailSettings
          actions={settingsActions}
          agentSettings={agentSettings ?? undefined}
          maintenanceSettings={spoonSettings ?? undefined}
          pending={{
            addingRemote: pending === 'addRemote',
            addingSecret: pending === 'addSecret',
            importingSecrets: pending === 'importSecrets',
            removingRemoteId: pending?.startsWith('remote:')
              ? pending.slice('remote:'.length)
              : undefined,
            removingSecretId: pending?.startsWith('secret:')
              ? pending.slice('secret:'.length)
              : undefined,
            savingSettings: pending === 'settings',
          }}
          providerProfiles={providerProfiles}
          remotes={remotes}
          secrets={secrets}
          spoon={spoon}
        />
      ) : null}
    </AppScreen>
  );
};

export default SpoonDetailRoute;
