import { useLocalSearchParams, useRouter } from 'expo-router'
import { usePreventScreenCapture } from 'expo-screen-capture'
import { useState } from 'react'
import {
  ActivityIndicator,
  Dimensions,
  Keyboard,
  KeyboardAvoidingView,
  Platform,
  Pressable,
  ScrollView as RNScrollView,
  TouchableWithoutFeedback,
} from 'react-native'
import Animated from 'react-native-reanimated'
import { SafeAreaView } from 'react-native-safe-area-context'
import UserAvatar from 'src/components/common/UserAvatar'
import { updateCredentials } from 'src/lib/api'
import { APP } from 'src/lib/branding'
import { useInstanceCapabilities } from 'src/lib/capabilities'
import { pickMedia, resizeImage, toUploadFile } from 'src/lib/media'
import { protocolAppHeader } from 'src/lib/protocol/wireGates'
import { objectToForm } from 'src/requests'
import { useAuth } from 'src/state/AuthProvider'
import { secureStore } from 'src/state/storage'
import {
  Button,
  Form,
  Input,
  Label,
  Text,
  TextArea,
  useTheme,
  useToastController,
  View,
  YStack,
} from 'tamagui'

const { width } = Dimensions.get('window')

// Same shape as handleLogin / handleSignup. verifyEmail is reachable via the
// `${APP.scheme}://verifyEmail?domain=…` deep link and forwards `domain`
// into `postRequest(domain, 'api/auth/…')`; a hostile deep link with a
// malformed `domain` would otherwise result in a request to an attacker-
// controlled host. Regex-valid attacker-owned domains are inherently outside
// the gate — the same limit applies to handleLogin's gate.
const DOMAIN_REGEX = /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/

// Signup is Pixelfed-only in v1 — every endpoint hit from this screen
// (`api/auth/app-code-verify`, `api/auth/onboarding`) is Pixelfed-exclusive.
// The wire gate (`X-PIXELFED-APP`) is sourced from `protocolAppHeader` so
// the single source of truth in `protocol/wireGates.ts` covers both
// `ApiContext` and the pre-auth signup path. When a Mastodon signup adapter
// lands, pass `protocol` in here instead of pinning it to 'pixelfed'.
const postRequest = async (
  domain: string,
  path: string,
  params = {},
  asForm = false,
  rawRes = false,
  _idempotency = false,
  appHeader = false
) => {
  const url = `https://${domain}/${path}`
  const headers: Record<string, string> = {
    Accept: 'application/json',
    'Content-Type': asForm ? 'multipart/form-data' : 'application/json',
    ...protocolAppHeader('pixelfed', appHeader),
  }

  const resp = await fetch(url, {
    method: 'POST',
    body: asForm ? objectToForm(params) : JSON.stringify(params),
    headers,
  })

  return rawRes ? resp : resp.json()
}

const VerificationStep = ({ onSubmit, isLoading, email, domain, updateCode }: any) => {
  const [code, setCode] = useState('')
  const [error, setError] = useState('')

  const handleSubmit = async () => {
    if (code.length !== 6) {
      setError('Please enter a valid 6-digit code')
      return
    }
    setError('')

    await postRequest(domain, 'api/auth/app-code-verify', {
      email: email,
      verify_code: code,
    })
      .then((res) => {
        if (res.status === 'success') {
          onSubmit(code)
          updateCode(code)
        } else {
          setError('Invalid code, please try again later')
        }
      })
      .catch(() => {
        setError('An error occurred, please try again later')
      })
  }

  return (
    <YStack width={width} p="$5" gap="$4" justifyContent="center">
      <YStack gap="$2">
        <Text fontSize="$8" fontWeight="700" color="$color">
          Check your email
        </Text>
        <Text fontSize="$4" color="$colorSecondary">
          We've sent a 6-digit code to{' '}
          <Text color="$color" fontWeight="600">
            {email}
          </Text>
        </Text>
      </YStack>

      <Form onSubmit={handleSubmit}>
        <YStack gap="$3">
          <Input
            size="$5"
            placeholder="000000"
            keyboardType="number-pad"
            maxLength={6}
            value={code}
            onChangeText={setCode}
            textAlign="center"
            letterSpacing={8}
            fontSize="$7"
            focusStyle={{ borderColor: '$borderColorStrong' }}
          />

          {error ? (
            <Text color="$red10" fontSize="$3">
              {error}
            </Text>
          ) : null}

          <Form.Trigger asChild disabled={isLoading}>
            <Button size="$4">
              {isLoading ? (
                <ActivityIndicator />
              ) : (
                <Button.Text fontWeight="600">Verify</Button.Text>
              )}
            </Button>
          </Form.Trigger>
        </YStack>
      </Form>
    </YStack>
  )
}

const DetailsStep = ({ onSubmit, isLoading, domain, code, email, setLoading }: any) => {
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [displayName, setDisplayName] = useState('')
  const [error, setError] = useState('')
  const router = useRouter()
  const toast = useToastController()

  const handleSubmit = async () => {
    if (!username || !password || !displayName) {
      setError('All fields are required')
      return
    }
    setError('')
    setLoading(true)

    try {
      await postRequest(domain, 'api/auth/onboarding', {
        email: email,
        verify_code: code,
        username: username,
        name: displayName,
        password: password,
      })
        .then((res) => {
          if (res.status === 'success') {
            const resolvedDomain = res.domain as string
            const authToken = res.access_token as string
            // Per-instance OAuth credentials are stored under the
            // resolved domain so any future account on this instance can
            // reuse them. The access/refresh tokens are forwarded to
            // `handleRegistration`, which finalises the account creation
            // (verify_credentials → AccountStore.upsert) once it knows the
            // userId. (Plan §6.2 / Phase 4 will migrate this end-to-end;
            // Phase 1 just keeps the surface compiling.)
            secureStore.setClientId(resolvedDomain, res.client_id.toString())
            secureStore.setClientSecret(resolvedDomain, res.client_secret)
            onSubmit({
              username,
              password,
              displayName,
              domain: resolvedDomain,
              authToken,
              refreshToken: res.refresh_token as string | undefined,
              expiresIn: res.expires_in as number | undefined,
            })
          } else {
            toast.show('Invalid code, please try again later', { preset: 'error' })
            router.back()
          }
        })
        .catch(() => {
          toast.show('Invalid code, please try again later', { preset: 'error' })
          router.back()
        })
    } catch (_error) {
      setError('An error occurred.')
    }
  }

  return (
    <YStack width={width} p="$5" gap="$4" justifyContent="center">
      <YStack gap="$2">
        <Text fontSize="$8" fontWeight="700" color="$color">
          Complete your profile
        </Text>
        <Text fontSize="$4" color="$colorSecondary">
          Choose how you'll appear in the community
        </Text>
      </YStack>

      <Form onSubmit={handleSubmit}>
        <YStack gap="$4">
          <YStack gap="$2">
            <Input
              size="$5"
              placeholder="Username"
              autoCapitalize="none"
              autoComplete="off"
              maxLength={30}
              value={username}
              onChangeText={setUsername}
              focusStyle={{ borderColor: '$borderColorStrong' }}
            />
            <Text fontSize="$2" color="$colorTertiary">
              Username must be 2-30 characters long, start/end with a letter or number,
              contain at least one letter, and may include a single dash, underscore, or
              period.
            </Text>
          </YStack>

          <YStack gap="$2">
            <Input
              size="$5"
              placeholder="Display name"
              autoCapitalize="none"
              autoComplete="off"
              maxLength={30}
              value={displayName}
              onChangeText={setDisplayName}
              focusStyle={{ borderColor: '$borderColorStrong' }}
            />
            <Text fontSize="$2" color="$colorTertiary">
              Display names can be up to 30 characters long.
            </Text>
          </YStack>

          <YStack gap="$2">
            <Input
              size="$5"
              placeholder="Password"
              secureTextEntry
              value={password}
              onChangeText={setPassword}
              focusStyle={{ borderColor: '$borderColorStrong' }}
            />
            <Text fontSize="$2" color="$colorTertiary">
              Pick a secure password that is 8 characters or longer.
            </Text>
          </YStack>

          {error ? (
            <Text color="$red10" fontSize="$3">
              {error}
            </Text>
          ) : null}

          <Form.Trigger asChild disabled={isLoading}>
            <Button size="$4">
              {isLoading ? (
                <ActivityIndicator />
              ) : (
                <Button.Text fontWeight="600">Create Account</Button.Text>
              )}
            </Button>
          </Form.Trigger>
        </YStack>
      </Form>
    </YStack>
  )
}

const WelcomeStep = ({ onSubmit, isLoading, domain: _domain }: any) => {
  const theme = useTheme()
  const [avatar, setAvatar] = useState<any>(null)
  const [avatarPayload, setAvatarPayload] = useState<any>(null)
  const [bio, setBio] = useState('')
  const [error, setError] = useState('')
  const capabilities = useInstanceCapabilities()

  const pickImage = async () => {
    const raws = await pickMedia({
      kinds: ['photo'],
      squareCrop: true,
      limit: 1,
    })
    if (!raws.length) return
    // Same allowlist story as Settings → Edit Profile: avatar endpoints don't
    // accept HEIC, so re-encode to JPEG / WebP via the shared pipeline. Cap
    // at 2 MB / 1024 px to match the typical avatar size limit.
    const normalized = await resizeImage(raws[0], {
      maxImageBytes: 2 * 1024 * 1024,
      maxImageWidth: 1024,
      supportedImageMimeTypes: capabilities.supportedImageMimeTypes,
    })
    setAvatar(normalized.uri)
    setAvatarPayload(toUploadFile({ uri: normalized.uri, mimeType: normalized.mimeType }))
  }

  const handleSubmit = async () => {
    setError('')
    try {
      onSubmit(avatarPayload, bio.length > 0 ? bio : false)
    } catch (_error) {
      setError('Failed to update profile. Please try again.')
    }
  }

  const handleSkip = () => {
    onSubmit()
  }

  return (
    <YStack width={width} p="$5" gap="$4" justifyContent="center">
      <YStack gap="$2">
        <Text fontSize="$8" fontWeight="700" color="$color">
          Welcome to {APP.name}!
        </Text>
        <Text fontSize="$4" color="$colorTertiary">
          Let's personalize your profile
        </Text>
      </YStack>

      <View alignSelf="center" my="$2">
        {avatar ? (
          <UserAvatar url={avatar} size="lg" onPress={pickImage} />
        ) : (
          <Pressable onPress={pickImage}>
            <View
              width={96}
              height={96}
              borderRadius={48}
              justifyContent="center"
              alignItems="center"
              borderWidth={1}
              borderColor="$borderColor"
              backgroundColor={theme.backgroundTertiary.val}
            >
              <Text fontSize="$4" color="$colorTertiary">
                Add Photo
              </Text>
            </View>
          </Pressable>
        )}
      </View>

      <YStack gap="$2" position="relative">
        <Label htmlFor="bio" fontSize="$3" color="$colorSecondary">
          Bio
        </Label>
        <TextArea
          id="bio"
          size="$4"
          minHeight={100}
          placeholder="Write a short bio..."
          value={bio}
          onChangeText={setBio}
          maxLength={150}
          focusStyle={{ borderColor: '$borderColorStrong' }}
        />
        <Text
          position="absolute"
          bottom="$2"
          right="$3"
          fontSize="$1"
          color={bio.length >= 150 ? '$red10' : '$colorTertiary'}
        >
          {bio.length}/150
        </Text>
      </YStack>

      {error ? (
        <Text color="$red10" fontSize="$3">
          {error}
        </Text>
      ) : null}

      <YStack gap="$2">
        <Button size="$4" onPress={handleSubmit} disabled={isLoading}>
          {isLoading ? (
            <ActivityIndicator color={theme.color.val} />
          ) : (
            <Button.Text fontWeight="600">Complete Profile</Button.Text>
          )}
        </Button>

        <Button size="$3" chromeless onPress={handleSkip} disabled={isLoading}>
          <Button.Text color="$colorTertiary">Skip for now</Button.Text>
        </Button>
      </YStack>
    </YStack>
  )
}

const SignUp = ({ navigation: _navigation }: any) => {
  // Block screenshots / screen recording on the verification surface — the
  // 6-digit code, the chosen username/password, and the avatar/bio inputs all
  // live behind this screen. Released automatically when the screen unmounts.
  usePreventScreenCapture('verifyEmail')
  const params = useLocalSearchParams<{ domain?: string; email?: string }>()
  const [code, setCode] = useState()
  const [currentStep, setCurrentStep] = useState(0)
  const [isLoading, setIsLoading] = useState(false)
  const { handleRegistration } = useAuth()
  const router = useRouter()
  const [registeredDomain, setRegisteredDomain] = useState<any>(null)
  const theme = useTheme()
  const toast = useToastController()

  // Refuse to mount the form steps if the deep-link `domain` is malformed.
  // The legitimate flow (handleSignup → openAuthSessionAsync → server
  // redirect → handleDeepLink) always emits a regex-valid host; a malformed
  // value can only come from a hostile deep link.
  const safeDomain =
    params.domain && DOMAIN_REGEX.test(params.domain) ? params.domain : null

  const handleVerificationSubmit = async (_code: any) => {
    setIsLoading(true)
    try {
      setCurrentStep(1)
    } catch (_error) {
    } finally {
      setIsLoading(false)
    }
  }

  // Land the new account in the registry as soon as the onboarding endpoint
  // hands us a token — *before* the optional avatar/bio step. The
  // WelcomeStep then runs against an active session, so the
  // `updateCredentials` call (which goes through `ContextForActiveAccount()`)
  // resolves correctly. This is what the plan §6.2 / Phase 4 means by
  // "fresh signup lands in the registry like a normal Add Account".
  const handleDetailsSubmit = async (details: any) => {
    setIsLoading(true)
    try {
      const ok = await handleRegistration(
        details.domain,
        details.authToken,
        details.refreshToken,
        details.expiresIn
      )
      if (!ok) {
        toast.show('Could not finish sign-up', {
          message: 'Authentication failed. Please try again.',
          preset: 'error',
          duration: 6000,
        })
        router.back()
        return
      }
      setRegisteredDomain(details.domain)
      setCurrentStep(2)
    } catch (_error) {
      toast.show('Could not finish sign-up', {
        message: 'Authentication failed. Please try again.',
        preset: 'error',
        duration: 6000,
      })
    } finally {
      setIsLoading(false)
    }
  }

  const handleWelcomeSubmit = async (avatar: any = false, bio: any = false) => {
    setIsLoading(true)
    try {
      // Avatar + bio go in a single `update_credentials` call now that the
      // helper accepts the full `UpdateCredentialsInput` shape — no second
      // `updateAvatar` round-trip and no React Query plumbing needed during
      // signup (the registry sync that follows happens separately on next
      // session).
      const input: Parameters<typeof updateCredentials>[0] = {}
      if (avatar) input.avatar = avatar
      if (bio && bio.length) input.note = bio
      if (Object.keys(input).length) await updateCredentials(input)

      router.replace('/(auth)/(tabs)/home')
    } catch (_error) {
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <SafeAreaView
      style={{ flex: 1, backgroundColor: theme.background.val }}
      edges={['top']}
    >
      <KeyboardAvoidingView
        behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
        style={{ flex: 1 }}
      >
        <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
          <RNScrollView
            contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}
            keyboardShouldPersistTaps="handled"
            showsVerticalScrollIndicator={false}
          >
            <Animated.View
              style={{
                flex: 1,
                flexDirection: 'row',
                transform: [{ translateX: -width * currentStep }],
                transitionProperty: 'transform',
                transitionDuration: 300,
                transitionTimingFunction: 'ease-in-out',
              }}
            >
              <VerificationStep
                onSubmit={handleVerificationSubmit}
                isLoading={isLoading}
                email={params.email}
                domain={safeDomain}
                updateCode={setCode}
              />

              <DetailsStep
                onSubmit={handleDetailsSubmit}
                setLoading={setIsLoading}
                isLoading={isLoading}
                email={params.email}
                domain={safeDomain}
                code={code}
              />

              <WelcomeStep
                onSubmit={handleWelcomeSubmit}
                isLoading={isLoading}
                domain={registeredDomain}
              />
            </Animated.View>
          </RNScrollView>
        </TouchableWithoutFeedback>
      </KeyboardAvoidingView>
    </SafeAreaView>
  )
}

export default SignUp
