import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ChangeEvent,
  type FormEvent,
} from 'react'
import * as Dialog from '@radix-ui/react-dialog'
import { X } from 'lucide-react'
import { useApolloClient, useMutation } from '@apollo/client/react'

import {
  CHANGE_ORGANISATION_MUTATION,
  CREATE_ORGANISATION_MUTATION,
  CREATE_UPLOAD_ORGANISATION_LOGO_URL_MUTATION,
  GET_ORGANISATION_QUERY,
} from '../../../../entities/employee/api/employee.graphql'
import type {
  ChangeOrganisationData,
  ChangeOrganisationVariables,
  CreateOrganisationData,
  CreateOrganisationVariables,
  CreateUploadOrganisationLogoUrlData,
  CreateUploadOrganisationLogoUrlVariables,
  GetOrganisationData,
  GetOrganisationVariables,
  Organisation,
  OrganisationPolicy,
} from '../../../../entities/employee/model/types'
import { MotionDialogContent } from '../../../../shared/ui/MotionDialog/MotionDialog'
import {
  OrganisationPolicyCard,
  defaultOrganisationPolicyDraft,
  mapOrganisationPolicyDraftToVariables,
  mapOrganisationPolicyToDraft,
  type OrganisationPolicyDraft,
} from '../../../OrganisationPage/components/OrganisationPolicyCard/OrganisationPolicyCard'

import { AvatarUpload } from '../AvatarUpload/AvatarUpload'
import { uploadMultipartFile } from '../../lib/uploadMultipartFile'

import './AddOrganisationModal.scss'

type AddOrganisationModalProps = {
  open: boolean
  mode?: 'create' | 'edit'
  organisation?: Organisation | null
  onOpenChange: (open: boolean) => void
  onSuccess?: () => void | Promise<void>
}

type OrganisationPolicyVariables = Omit<
  CreateOrganisationVariables,
  'name'
>

function getPolicyVariables(
  policy?: OrganisationPolicy | OrganisationPolicyDraft | null,
): OrganisationPolicyVariables {
  return mapOrganisationPolicyDraftToVariables(
    mapOrganisationPolicyToDraft(policy),
  )
}

export function AddOrganisationModal({
  open,
  mode = 'create',
  organisation = null,
  onOpenChange,
  onSuccess,
}: AddOrganisationModalProps) {
  const client = useApolloClient()

  const [name, setName] = useState('')
  const [policy, setPolicy] = useState<OrganisationPolicyDraft>(
    defaultOrganisationPolicyDraft,
  )

  const [logoFile, setLogoFile] = useState<File | null>(null)
  const [logoPreviewUrl, setLogoPreviewUrl] = useState('')
  const logoPreviewUrlRef = useRef('')

  const [createUploadOrganisationLogoUrl] = useMutation<
    CreateUploadOrganisationLogoUrlData,
    CreateUploadOrganisationLogoUrlVariables
  >(CREATE_UPLOAD_ORGANISATION_LOGO_URL_MUTATION)

  const isEditMode = mode === 'edit'

  const [createOrganisation, { loading: isCreating, error: createError }] =
    useMutation<CreateOrganisationData, CreateOrganisationVariables>(
      CREATE_ORGANISATION_MUTATION,
    )

  const [changeOrganisation, { loading: isChanging, error: changeError }] =
    useMutation<ChangeOrganisationData, ChangeOrganisationVariables>(
      CHANGE_ORGANISATION_MUTATION,
    )
  const [isLogoUploading, setIsLogoUploading] = useState(false)
  const isLoading = isCreating || isChanging || isLogoUploading
  const mutationError = createError || changeError
  const modalClassName = useMemo(
    () =>
      `add-organisation-modal ${
        !isEditMode ? 'add-organisation-modal--with-policy' : 'add-organisation-modal--without-policy'
      }`,
    [isEditMode],
  )
  const formClassName = useMemo(
    () =>
      `add-organisation-form ${
        !isEditMode ? 'add-organisation-form--with-policy' : ''
      }`,
    [isEditMode],
  )
  const logoAvatarUrl = useMemo(
    () => logoPreviewUrl || organisation?.logoUrl || '',
    [logoPreviewUrl, organisation?.logoUrl],
  )
  const organisationInitials = useMemo(
    () => getOrganisationInitials(name),
    [name],
  )
  const handleNameChange = useCallback((
    event: ChangeEvent<HTMLInputElement>,
  ) => {
    setName(event.target.value)
  }, [])

  useEffect(() => {
    logoPreviewUrlRef.current = logoPreviewUrl
  }, [logoPreviewUrl])

  useEffect(() => {
    return () => {
      if (logoPreviewUrlRef.current) {
        URL.revokeObjectURL(logoPreviewUrlRef.current)
      }
    }
  }, [])

  function getOrganisationInitials(nameValue: string) {
    const words = nameValue.trim().split(/\s+/).filter(Boolean)

    if (words.length >= 2) {
      return `${words[0][0] ?? ''}${words[1][0] ?? ''}`.toUpperCase()
    }

    return nameValue.trim().slice(0, 2).toUpperCase() || 'ОР'
  }

  function handleLogoChange(file: File, previewUrl: string) {
    if (logoPreviewUrlRef.current) {
      URL.revokeObjectURL(logoPreviewUrlRef.current)
      logoPreviewUrlRef.current = ''
    }

    setLogoFile(file)
    setLogoPreviewUrl(previewUrl)
  }

  function handleLogoReset() {
    resetLogoDraft()
  }

  function resetLogoDraft() {
    if (logoPreviewUrlRef.current) {
      URL.revokeObjectURL(logoPreviewUrlRef.current)
      logoPreviewUrlRef.current = ''
    }

    setLogoFile(null)
    setLogoPreviewUrl('')
  }

  async function uploadLogoForOrganisation(orgId: string) {
    if (!logoFile) return

    setIsLogoUploading(true)

    try {
      const uploadUrlResult = await createUploadOrganisationLogoUrl({
        variables: {
          orgId,
        },
      })

      const uploadUrl = uploadUrlResult.data?.createUploadOrganisationLogoUrl

      if (!uploadUrl) {
        throw new Error('Не удалось получить ссылку для загрузки логотипа')
      }

      await uploadMultipartFile(uploadUrl, 'logo', logoFile)
    } finally {
      setIsLogoUploading(false)
    }
  }

  useEffect(() => {
    if (!open) return

    resetLogoDraft()

    if (isEditMode && organisation) {
      setName(organisation.name ?? '')
      return
    }

    setName('')
    setPolicy(defaultOrganisationPolicyDraft)
  }, [open, isEditMode, organisation])

  async function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()

    const trimmedName = name.trim()

    if (!trimmedName) return

    if (isEditMode) {
      if (!organisation) return

      let currentPolicy = organisation.policy

      if (!currentPolicy) {
        const result = await client.query<
          GetOrganisationData,
          GetOrganisationVariables
        >({
          query: GET_ORGANISATION_QUERY,
          variables: {
            id: String(organisation.id),
          },
          fetchPolicy: 'network-only',
        })

        currentPolicy = result.data?.getOrganisation?.policy ?? null
      }

      const policyVariables = getPolicyVariables(currentPolicy)

      try {
        await changeOrganisation({
          variables: {
            id: String(organisation.id),
            name: trimmedName,
            ...policyVariables,
          },
          refetchQueries: [
            {
              query: GET_ORGANISATION_QUERY,
              variables: {
                id: String(organisation.id),
              },
            },
          ],
        })

        if (logoFile) {
          try {
            await uploadLogoForOrganisation(String(organisation.id))
          } catch (logoError) {
            console.error(
              'Организация изменена, но логотип не загрузился',
              logoError,
            )
          }
        }

        try {
          await onSuccess?.()
        } catch (successError) {
          console.error(
            'Организация изменена, но refetch не сработал',
            successError,
          )
        }

        onOpenChange(false)
      } catch (changeError) {
        console.error('Не удалось изменить организацию', changeError)
      }

      return
    }

    const policyVariables = getPolicyVariables(policy)

    try {
      const result = await createOrganisation({
        variables: {
          name: trimmedName,
          ...policyVariables,
        },
      })

      const createdOrganisationId = result.data?.createOrganisation.id

      if (!createdOrganisationId) {
        throw new Error('Организация создана, но id не вернулся')
      }

      if (logoFile) {
        try {
          await uploadLogoForOrganisation(String(createdOrganisationId))
        } catch (logoError) {
          console.error(
            'Организация создана, но логотип не загрузился',
            logoError,
          )
        }
      }

      try {
        await onSuccess?.()
      } catch (successError) {
        console.error(
          'Организация создана, но refetch не сработал',
          successError,
        )
      }

      onOpenChange(false)
    } catch (createError) {
      console.error('Не удалось создать организацию', createError)
    }
  }

  return (
    <Dialog.Root open={open} onOpenChange={onOpenChange}>
      <MotionDialogContent
        open={open}
        overlayClassName="add-organisation-modal__overlay"
        contentClassName={modalClassName}
        motionPreset="fade"
      >
        <div className="add-organisation-modal__header">
          <div>
            <Dialog.Title className="add-organisation-modal__title">
              {isEditMode
                ? 'Редактировать организацию'
                : 'Добавить организацию'}
            </Dialog.Title>

            <Dialog.Description className="add-organisation-modal__description">
              {isEditMode
                ? 'Изменение данных организации'
                : 'Создание организации для привязки пользователей и устройств'}
            </Dialog.Description>
          </div>

          <Dialog.Close
            className="add-organisation-modal__close"
            type="button"
            disabled={isLoading}
          >
            <X size={20} />
          </Dialog.Close>
        </div>

        <form
          className={formClassName}
          onSubmit={handleSubmit}
        >
          <div className="add-organisation-form__main">
            <AvatarUpload
              avatarUrl={logoAvatarUrl}
              allowCamera={false}
              initials={organisationInitials}
              disabled={isLoading}
              hasSelectedFile={Boolean(logoFile)}
              title="Логотип организации"
              description="PNG, JPG или GIF"
              chooseText="Выбрать логотип"
              replaceText="Заменить логотип"
              alt="Логотип организации"
              onChange={handleLogoChange}
              onReset={handleLogoReset}
            />

            <label className="add-organisation-field">
              <span>Название организации</span>

              <input
                value={name}
                onChange={handleNameChange}
                placeholder="Название организации"
                required
              />
            </label>
          </div>

          {!isEditMode && (
            <OrganisationPolicyCard
              variant="form"
              title="Начальная политика"
              description="Будет применяться к устройствам этой организации"
              value={policy}
              disabled={isLoading}
              onChange={setPolicy}
            />
          )}

          {mutationError && (
            <div className="add-organisation-error">
              {isEditMode
                ? 'Не удалось сохранить организацию. Проверьте данные.'
                : 'Не удалось добавить организацию. Проверьте данные.'}
            </div>
          )}

          <div className="add-organisation-modal__footer">
            <Dialog.Close
              className="add-organisation-cancel"
              type="button"
              disabled={isLoading}
            >
              Отмена
            </Dialog.Close>

            <button
              className="add-organisation-submit"
              type="submit"
              disabled={isLoading || !name.trim()}
            >
              {isLoading
                ? isEditMode
                  ? 'Сохранение...'
                  : 'Добавление...'
                : isEditMode
                  ? 'Сохранить'
                  : 'Добавить'}
            </button>
          </div>
        </form>
      </MotionDialogContent>
    </Dialog.Root>
  )
}
