import {useState} from 'react'
import {View} from 'react-native'
import {Image} from 'expo-image'
import {
  type ChatBskyGroupDefs,
  moderateProfile,
  type ModerationOpts,
} from '@atproto/api'
import {Plural, Trans, useLingui} from '@lingui/react/macro'

import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {shareUrl} from '#/lib/sharing'
import {useCreateJoinLink} from '#/state/queries/messages/create-join-link'
import {useDisableJoinLink} from '#/state/queries/messages/disable-join-link'
import {useEditJoinLink} from '#/state/queries/messages/edit-join-link'
import {useEnableJoinLink} from '#/state/queries/messages/enable-join-link'
import {atoms as a, native, useTheme, web} from '#/alf'
import {
  Button,
  ButtonIcon,
  ButtonText,
  StackedButton,
} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {
  type ConvoWithDetails,
  type GroupConvoMember,
} from '#/components/dms/util'
import * as Toggle from '#/components/forms/Toggle'
import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRightIcon} from '#/components/icons/Arrow'
import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight'
import {ChainLinkBroken_Stroke2_Corner0_Rounded as ChainLinkBrokenIcon} from '#/components/icons/ChainLink'
import {EditBig_Stroke2_Corner2_Rounded as EditIcon} from '#/components/icons/EditBig'
import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env'
import {CopyTextButton} from './CopyTextButton'
import {EditTextButton} from './EditTextButton'

const infoImage = require('../../../../assets/images/chat-invite-friends.webp')

enum Step {
  INFO,
  GENERATE,
  MANAGE,
  CONFIRM_DISABLE,
}

export function InviteLinkDialog({
  convo,
  control,
  owner,
  isOwner,
  moderationOpts,
}: {
  convo: Extract<ConvoWithDetails, {kind: 'group'}>
  control: Dialog.DialogOuterProps['control']
  owner: GroupConvoMember
  isOwner: boolean
  moderationOpts: ModerationOpts
}) {
  const t = useTheme()
  const {t: l, i18n} = useLingui()
  const ax = useAnalytics()

  const convoId = convo.view.id

  const ownerName = createSanitizedDisplayName(
    owner,
    false,
    moderateProfile(owner, moderationOpts).ui('displayName'),
  )

  const {joinLink} = convo.details

  const defaultStep = joinLink ? Step.MANAGE : Step.INFO
  const defaultWhoCanJoin = joinLink ? joinLinkToKey(joinLink) : 'anyone'

  const [step, setStep] = useState(defaultStep)
  const [whoCanJoin, setWhoCanJoin] = useState(defaultWhoCanJoin)

  // Resync local state when the server-side join link rules change (mutation
  // success, refetch, or change from another client). Keyed on the rule string
  // so identity-only refetches don't bump a user mid-edit.
  const joinLinkRuleKey = joinLink ? joinLinkToKey(joinLink) : null
  const [prevKey, setPrevKey] = useState(joinLinkRuleKey)
  if (joinLinkRuleKey !== prevKey) {
    setStep(joinLinkRuleKey ? Step.MANAGE : Step.INFO)
    setWhoCanJoin(joinLinkRuleKey ?? 'anyone')
    setPrevKey(joinLinkRuleKey)
  }

  const {openComposer} = useOpenComposer()

  const {mutate: createJoinLink, isPending: isCreating} = useCreateJoinLink(
    convoId,
    {
      onSuccess: () => {
        ax.metric('groupchat:owner:inviteLink:create', {convoId})
        setStep(Step.MANAGE)
      },
      onError: () => {
        Toast.show(l`Failed to create invite link`, {
          type: 'error',
        })
      },
    },
  )
  const {mutate: editJoinLink, isPending: isEditing} = useEditJoinLink(
    convoId,
    {
      onSuccess: () => {
        setStep(Step.MANAGE)
      },
      onError: () => {
        Toast.show(l`Failed to edit invite link`, {
          type: 'error',
        })
      },
    },
  )
  const {mutate: disableJoinLink, isPending: isDisabling} = useDisableJoinLink(
    convoId,
    {
      onSuccess: () => {
        ax.metric('groupchat:owner:inviteLink:disable', {convoId})
      },
      onError: () => {
        Toast.show(l`Failed to disable invite link`, {
          type: 'error',
        })
      },
    },
  )
  const {mutate: enableJoinLink, isPending: isEnabling} = useEnableJoinLink(
    convoId,
    {
      onError: () => {
        Toast.show(l`Failed to enable invite link`, {
          type: 'error',
        })
      },
    },
  )
  const isSaving = isCreating || isEditing

  const whoCanJoinOptions = [
    {
      name: 'anyone',
      owner: l`Anyone can join instantly`,
      member: l`Anyone can join instantly`,
    },
    {
      name: 'anyone:requireApproval',
      owner: l`Anyone can request to join`,
      member: l`Anyone can request to join`,
    },
    {
      name: 'followedByOwner',
      owner: l`People I follow can join instantly`,
      member: l`People ${ownerName} follows can join instantly`,
    },
    {
      name: 'followedByOwner:requireApproval',
      owner: l`People I follow can request to join`,
      member: l`People ${ownerName} follows can request to join`,
    },
  ]

  let content: React.ReactNode = null
  let header: string | null = null
  let image: React.ReactNode = null
  switch (step) {
    case Step.INFO: {
      image = (
        <Image
          source={infoImage}
          style={[
            web(a.rounded_md),
            native(a.rounded_xl),
            a.mb_lg,
            a.w_full,
            {aspectRatio: 706 / 418},
            web({marginTop: 4}),
            native({marginTop: -8}),
          ]}
          accessibilityIgnoresInvertColors
        />
      )
      header = l`Invite link`
      content = (
        <>
          <View style={[a.gap_lg]}>
            <Text style={[a.text_md, a.leading_snug]}>
              <Trans>
                An invite link lets people join this group chat without being
                added directly. You control who can join the chat. You can
                disable the link at any time.
              </Trans>
            </Text>
            <Text style={[a.text_md, a.leading_snug]}>
              <Trans>
                Group chats can only have a maximum of{' '}
                <Plural value={convo.details.memberLimit} other="# people" />.
              </Trans>
            </Text>
            <Text style={[a.text_md, a.leading_snug]}>
              <Trans>
                Your name, avatar, the name of the group chat, and the number of
                members will be visible to everyone.
              </Trans>
            </Text>
          </View>
          <View style={[a.mt_4xl]}>
            <Button
              label={l`Get started`}
              color="primary"
              size="large"
              onPress={() => {
                setStep(Step.GENERATE)
              }}>
              <ButtonText>
                <Trans>Get started</Trans>
              </ButtonText>
              <ButtonIcon icon={ArrowRightIcon} />
            </Button>
          </View>
        </>
      )
      break
    }
    case Step.GENERATE: {
      const linkEnabled = joinLink?.enabledStatus === 'enabled'
      const linkHasChanged = linkEnabled && joinLinkRuleKey !== whoCanJoin

      header = linkEnabled ? l`Update invite link` : l`Generate invite link`
      content = (
        <>
          <Text style={[a.text_md]}>
            <Trans>Choose who can join this group chat and how.</Trans>
          </Text>
          <View style={[a.mt_lg]}>
            <Toggle.Group
              label={l`Who can join this group chat and how`}
              type="radio"
              values={[whoCanJoin]}
              onChange={([value]) => setWhoCanJoin(value)}>
              <View style={[a.gap_xs]}>
                {whoCanJoinOptions.map(option => (
                  <Toggle.Item
                    key={option.name}
                    highlightRow
                    label={isOwner ? option.owner : option.member}
                    name={option.name}
                    style={[a.flex_1]}>
                    {({selected}) => (
                      <Toggle.RadioWithLabel
                        label={isOwner ? option.owner : option.member}
                        selected={selected}
                      />
                    )}
                  </Toggle.Item>
                ))}
              </View>
            </Toggle.Group>
          </View>
          <View style={[a.mt_4xl]}>
            <Button
              label={
                linkEnabled
                  ? linkHasChanged
                    ? l`Update invite link`
                    : l`Back`
                  : l`Generate invite link`
              }
              color={linkEnabled && !linkHasChanged ? 'secondary' : 'primary'}
              size="large"
              disabled={isSaving}
              onPress={() => {
                const {joinRule, requireApproval} = keyToJoinLink(whoCanJoin)
                if (linkEnabled) {
                  if (!linkHasChanged) {
                    setStep(Step.MANAGE)
                    return
                  }
                  editJoinLink({
                    joinRule,
                    requireApproval,
                  })
                } else {
                  createJoinLink({
                    joinRule,
                    requireApproval,
                  })
                }
              }}>
              <ButtonText>
                {linkEnabled
                  ? linkHasChanged
                    ? l`Update invite link`
                    : l`Back`
                  : l`Generate invite link`}
              </ButtonText>
              {linkHasChanged && (
                <ButtonIcon icon={isSaving ? Loader : ArrowRightIcon} />
              )}
            </Button>
          </View>
        </>
      )
      break
    }
    case Step.MANAGE: {
      const linkEnabled = joinLink?.enabledStatus === 'enabled'
      const linkDisabled = joinLink?.enabledStatus === 'disabled'
      const joinLinkURI = joinLink?.code
        ? `https://bsky.app/chat/${joinLink.code}`
        : 'https://bsky.app/'
      const createdAt = joinLink ? new Date(joinLink.createdAt) : null
      const currentOption =
        whoCanJoinOptions.find(
          o => o.name === (joinLink ? joinLinkToKey(joinLink) : null),
        ) ?? whoCanJoinOptions[0]
      const ownerValue = currentOption?.owner ?? whoCanJoinOptions[0].owner
      const memberValue = currentOption?.member ?? whoCanJoinOptions[0].member
      header = linkEnabled ? l`Invite link` : l`Invite link disabled`
      content = (
        <>
          <View style={[native(a.mt_lg)]}>
            <CopyTextButton
              disabled={linkDisabled || !joinLink?.code}
              label={l`Invite link`}
              value={joinLinkURI}
              onPress={() => {
                ax.metric('groupchat:inviteLink:shareButton:press', {
                  convoId,
                  method: 'copy',
                })
              }}>
              <Text
                numberOfLines={1}
                style={[
                  a.mr_xs,
                  a.text_md,
                  linkDisabled ? t.atoms.text_contrast_low : t.atoms.text,
                ]}>
                {joinLinkURI}
              </Text>
            </CopyTextButton>
            {createdAt ? (
              <Text style={[a.mt_xs, a.text_xs, t.atoms.text_contrast_medium]}>
                <Trans>
                  Created{' '}
                  {i18n.date(createdAt, {
                    dateStyle: 'long',
                    timeStyle: 'short',
                  })}
                </Trans>
              </Text>
            ) : null}
          </View>
          {linkEnabled ? (
            isOwner ? (
              <View style={[a.mt_lg]}>
                <EditTextButton
                  label={l`Edit link settings`}
                  value={ownerValue}
                  onPress={() => setStep(Step.GENERATE)}>
                  <View style={[a.flex_1]}>
                    <Text style={[a.text_sm]}>{ownerValue}</Text>
                  </View>
                </EditTextButton>
              </View>
            ) : (
              <Text style={[a.mt_sm, a.mb_sm, a.text_sm]}>{memberValue}</Text>
            )
          ) : null}
          {linkEnabled ? (
            <View style={[a.flex_row, a.justify_between, a.gap_sm, a.mt_lg]}>
              {isOwner ? (
                <StackedButton
                  label={l`Disable`}
                  icon={ChainLinkBrokenIcon}
                  color="negative_subtle"
                  style={[a.flex_1]}
                  onPress={() => setStep(Step.CONFIRM_DISABLE)}>
                  <Trans>Disable</Trans>
                </StackedButton>
              ) : null}
              <StackedButton
                disabled={linkDisabled}
                label={l`Post link`}
                icon={EditIcon}
                color="primary_subtle"
                style={[a.flex_1]}
                onPress={() => {
                  ax.metric('groupchat:inviteLink:shareButton:press', {
                    convoId,
                    method: 'post',
                  })
                  control.close(() => {
                    openComposer({
                      text: joinLinkURI,
                      logContext: 'Other',
                    })
                  })
                }}>
                <Trans>Post link</Trans>
              </StackedButton>
              <StackedButton
                disabled={linkDisabled}
                label={l`Share`}
                icon={ArrowShareRightIcon}
                color="primary_subtle"
                style={[a.flex_1]}
                onPress={() => {
                  ax.metric('groupchat:inviteLink:shareButton:press', {
                    convoId,
                    method: 'native',
                  })
                  void shareUrl(joinLinkURI)
                }}>
                <Trans>Share</Trans>
              </StackedButton>
            </View>
          ) : (
            <View style={[a.gap_md, a.mt_lg]}>
              <Button
                disabled={isEnabling || isDisabling}
                label={l`Re-enable invite link`}
                color="primary"
                size="large"
                onPress={() => {
                  enableJoinLink()
                }}>
                <ButtonText>
                  <Trans>Re-enable link</Trans>
                </ButtonText>
                {isEnabling && <ButtonIcon icon={Loader} />}
              </Button>
              <Button
                disabled={isEnabling || isDisabling}
                label={l`Generate new invite link`}
                color="secondary"
                size="large"
                onPress={() => setStep(Step.GENERATE)}>
                <ButtonText>
                  <Trans>Generate new link</Trans>
                </ButtonText>
              </Button>
            </View>
          )}
        </>
      )
      break
    }
    case Step.CONFIRM_DISABLE: {
      content = (
        <>
          <View style={[a.align_center, a.justify_center, a.mb_lg]}>
            <ChainLinkBrokenIcon fill={t.palette.negative_500} size="3xl" />
          </View>
          <Text
            style={[
              a.flex_1,
              a.pb_sm,
              a.text_center,
              a.text_lg,
              a.font_bold,
              a.leading_snug,
            ]}>
            <Trans>Disable this invite link?</Trans>
          </Text>
          <Text style={[a.pb_2xl, a.text_center, a.text_sm, a.leading_snug]}>
            <Trans>
              Anyone who has it will no longer be able to join or request to
              join. You can always create a new one.
            </Trans>
          </Text>
          <View style={[a.w_full, a.gap_md, a.justify_end]}>
            <Button
              color="negative"
              disabled={isDisabling}
              size="large"
              label={l`Disable link`}
              onPress={() => {
                disableJoinLink()
                setStep(Step.MANAGE)
              }}>
              <ButtonText>
                <Trans>Disable link</Trans>
              </ButtonText>
            </Button>
            <Button
              color="secondary"
              size="large"
              label={l`Cancel`}
              onPress={() => {
                setStep(Step.MANAGE)
              }}>
              <ButtonText>{l`Cancel`}</ButtonText>
            </Button>
          </View>
        </>
      )
      break
    }
  }

  if (!isOwner && (!joinLink || joinLink.enabledStatus === 'disabled')) {
    header = l`Invite link`
    content = (
      <>
        <View style={[a.mt_lg]}>
          <Text style={[a.text_sm]}>
            <Trans>There is no invite link for this group chat.</Trans>
          </Text>
        </View>
        <View style={[a.gap_md, a.mt_lg]}>
          <Button
            label={l`Close`}
            color="primary"
            size="large"
            onPress={() => control.close()}>
            <ButtonText>
              <Trans>Close</Trans>
            </ButtonText>
          </Button>
        </View>
      </>
    )
  }

  return (
    <Dialog.Outer
      control={control}
      onClose={() => {
        setStep(defaultStep)
        setWhoCanJoin(defaultWhoCanJoin)
      }}
      nativeOptions={{preventExpansion: true}}>
      <Dialog.Handle />
      <Dialog.ScrollableInner
        header={
          <View>
            <View style={[IS_WEB ? [a.px_2xl, a.pt_xl] : {paddingTop: 10}]}>
              {image}
              <Text style={[a.font_bold, a.text_2xl, a.mb_sm]}>{header}</Text>
            </View>
            <Dialog.Close />
          </View>
        }
        label={l`Group chat invite link dialog`}
        style={web({maxWidth: 400})}
        contentContainerStyle={web(a.pt_0)}>
        {content}
      </Dialog.ScrollableInner>
    </Dialog.Outer>
  )
}

function joinLinkToKey(joinLink: ChatBskyGroupDefs.JoinLinkView): string {
  return `${joinLink.joinRule}${joinLink.requireApproval ? ':requireApproval' : ''}`
}

function keyToJoinLink(
  key: string,
): Pick<ChatBskyGroupDefs.JoinLinkView, 'joinRule' | 'requireApproval'> {
  const [joinRule, requireApproval] = key.split(':')
  return {
    joinRule,
    requireApproval: requireApproval === 'requireApproval',
  }
}
