// src/contexts/AuthContext.js
import {
  createContext,
  useState,
  useCallback,
  useEffect,
  useContext
} from 'react'
import axios from 'axios'
import {
  message,
  Modal,
  notification,
  Progress,
  Button,
  Space,
  Typography
} from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import PropTypes from 'prop-types'
import ExclamationOctogonIcon from '../../Icons/ExclamationOctagonIcon'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import config from '../../../config'
import loglevel from 'loglevel'
import { ElectronContext } from './ElectronContext'
import { useLocation, useNavigate } from 'react-router-dom'
import {
  getAuthCookies,
  setAuthCookies,
  clearAuthCookies,
  areCookiesEnabled,
  validateAuthCookies,
  setupCookieSync,
  checkAuthCookiesExpiry
} from '../../../utils/cookies'

const logger = loglevel.getLogger('ApiServerContext')
logger.setLevel(config.logLevel)

const AuthContext = createContext()

const { Text } = Typography

const AuthProvider = ({ children }) => {
  const [messageApi, contextHolder] = message.useMessage()
  const [notificationApi, notificationContextHolder] =
    notification.useNotification()
  const [authenticated, setAuthenticated] = useState(false)
  const [initialized, setInitialized] = useState(false)
  const [retreivedTokenFromCookies, setRetreivedTokenFromCookies] =
    useState(false)
  const [loading, setLoading] = useState(false)
  const [token, setToken] = useState(null)
  const [expiresAt, setExpiresAt] = useState(null)
  const [userProfile, setUserProfile] = useState(null)
  const [showSessionExpiredModal, setShowSessionExpiredModal] = useState(false)
  const [showUnauthorizedModal, setShowUnauthorizedModal] = useState(false)
  const [showAuthErrorModal, setShowAuthErrorModal] = useState(false)
  const [authError, setAuthError] = useState(null)
  const { openExternalUrl, isElectron } = useContext(ElectronContext)
  const location = useLocation()
  const navigate = useNavigate()

  var redirectType = 'web'

  if (isElectron == true && import.meta.env.MODE == 'development') {
    redirectType = 'app-localhost'
  }

  if (isElectron == true && import.meta.env.MODE != 'development') {
    redirectType = 'app-scheme'
  }

  // Check if cookies are enabled and show warning if not
  useEffect(() => {
    if (!areCookiesEnabled()) {
      messageApi.warning(
        'Cookies are disabled. Login state may not persist between tabs.'
      )
    }
  }, [messageApi])

  // Read token from cookies if present
  useEffect(() => {
    try {
      // First validate existing cookies to clean up expired ones
      if (validateAuthCookies()) {
        const {
          token: storedToken,
          expiresAt: storedExpiresAt,
          user: storedUser
        } = getAuthCookies()
        console.log('Retrieved from cookies:', {
          storedUser,
          storedToken,
          storedExpiresAt
        })

        setToken(storedToken)
        setUserProfile(storedUser)
        setExpiresAt(storedExpiresAt)
        setAuthenticated(true)
      } else {
        setAuthenticated(false)
        setUserProfile(null)
        setShowUnauthorizedModal(true)
      }
    } catch (error) {
      console.error('Error reading auth cookies:', error)
      clearAuthCookies()
      setAuthenticated(false)
      setUserProfile(null)
      setShowUnauthorizedModal(true)
    }
    setRetreivedTokenFromCookies(true)
  }, [])

  // Set up cookie synchronization between tabs
  useEffect(() => {
    const cleanupCookieSync = setupCookieSync(() => {
      // When cookies change in another tab, re-validate and update state
      try {
        if (validateAuthCookies()) {
          const {
            token: newToken,
            expiresAt: newExpiresAt,
            user: newUser
          } = getAuthCookies()
          if (
            newToken !== token ||
            newExpiresAt !== expiresAt ||
            JSON.stringify(newUser) !== JSON.stringify(userProfile)
          ) {
            setToken(newToken)
            setExpiresAt(newExpiresAt)
            setUserProfile(newUser)
            setAuthenticated(true)
            console.log('Auth state synchronized from another tab')
          }
        } else {
          // Cookies are invalid, clear state
          setToken(null)
          setExpiresAt(null)
          setUserProfile(null)
          setAuthenticated(false)
          setShowUnauthorizedModal(true)
          console.log(
            'Auth state cleared due to invalid cookies from another tab'
          )
        }
      } catch (error) {
        console.error('Error syncing auth state:', error)
      }
    })

    return cleanupCookieSync
  }, [token, expiresAt, userProfile])

  const logout = useCallback((redirectUri = '/login') => {
    setAuthenticated(false)
    setToken(null)
    setExpiresAt(null)
    setUserProfile(null)
    clearAuthCookies()
    window.location.href = `${config.backendUrl}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`
  }, [])

  // Login using query parameters
  const loginWithSSO = useCallback(
    (redirectUri = location.pathname + location.search) => {
      messageApi.info('Logging in with tombutcher.work')
      const loginUrl = `${config.backendUrl}/auth/${redirectType}/login?redirect_uri=${encodeURIComponent(redirectUri)}`
      if (isElectron) {
        console.log('Opening external url...')
        openExternalUrl(loginUrl)
        setLoading(true)
      } else {
        console.log('Redirecting...')
        window.location.href = loginUrl
      }
    },
    [
      redirectType,
      messageApi,
      openExternalUrl,
      isElectron,
      location.search,
      location.pathname
    ]
  )

  const getLoginToken = useCallback(
    async (code) => {
      setLoading(true)
      setShowUnauthorizedModal(false)
      setShowSessionExpiredModal(false)
      setAuthError(null)
      try {
        // Make a call to your backend to check auth status
        const response = await axios.get(
          `${config.backendUrl}/auth/${redirectType}/token?code=${code}`
        )

        if (response.status === 200 && response.data) {
          logger.debug('Got auth token!')
          const authData = response.data

          setToken(authData.access_token)
          setExpiresAt(authData.expires_at)
          setUserProfile(authData)

          // Store in cookies for persistence between tabs
          const cookieSuccess = setAuthCookies(authData)
          if (!cookieSuccess) {
            messageApi.warning(
              'Authentication successful but failed to save login state. You may need to log in again if you close this tab.'
            )
          }

          const searchParams = new URLSearchParams(location.search)
          searchParams.delete('authCode')
          const newSearch = searchParams.toString()
          const newPath = location.pathname + (newSearch ? `?${newSearch}` : '')
          navigate(newPath, { replace: true })
        } else {
          setAuthenticated(false)
          setAuthError('Failed to authenticate user.')
          setShowAuthErrorModal(true)
        }
      } catch (error) {
        logger.debug('Auth check failed', error)
        if (error.response?.status === 401) {
          setShowUnauthorizedModal(true)
        } else {
          const errorMessage =
            error?.response?.data?.error ||
            'Error connecting to authentication service.'
          const fullStop = errorMessage.endsWith('.')
          setAuthError(`${errorMessage}${!fullStop && '.'}`)
          setShowAuthErrorModal(true)
        }
        setAuthenticated(false)
      } finally {
        setLoading(false)
      }
    },
    [isElectron, navigate, location.search, location.pathname, messageApi]
  )

  // Function to check if the user is logged in
  const checkAuthStatus = useCallback(async () => {
    setLoading(true)
    setAuthError(null)
    try {
      // Make a call to your backend to check auth status
      const response = await axios.get(`${config.backendUrl}/auth/user`, {
        headers: {
          Authorization: `Bearer ${token}`
        }
      })

      if (response.status === 200 && response.data) {
        logger.debug('Got auth token!')
        const authData = response.data

        setToken(authData.access_token)
        setExpiresAt(authData.expires_at)
        setUserProfile(authData)

        // Update cookies with fresh data
        const cookieSuccess = setAuthCookies(authData)
        if (!cookieSuccess) {
          messageApi.warning(
            'Failed to update login state. You may need to log in again if you close this tab.'
          )
        }
      } else {
        setAuthenticated(false)
        setAuthError('Failed to authenticate user.')
        setShowAuthErrorModal(true)
      }
    } catch (error) {
      logger.debug('Auth check failed', error)
      if (error.response?.status === 401) {
        setShowUnauthorizedModal(true)
      } else {
        setAuthError('Error connecting to authentication service.')
        setShowAuthErrorModal(true)
      }
      setAuthenticated(false)
    } finally {
      setLoading(false)
    }
  }, [token, messageApi])

  const setUnauthenticated = () => {
    setToken(null)
    setExpiresAt(null)
    setUserProfile(null)
    clearAuthCookies()
    setShowUnauthorizedModal(true)
  }

  const refreshToken = useCallback(async () => {
    try {
      const response = await axios.get(`${config.backendUrl}/auth/refresh`, {
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${token}`
        }
      })
      if (response.status === 200 && response.data) {
        const authData = response.data

        setToken(authData.access_token)
        setExpiresAt(authData.expires_at)

        // Update cookies with fresh token data
        const cookieSuccess = setAuthCookies(authData)
        if (!cookieSuccess) {
          messageApi.warning(
            'Failed to update login state. You may need to log in again if you close this tab.'
          )
        }
      }
    } catch (error) {
      console.error('Token refresh failed', error)
    }
  }, [token, messageApi])

  const handleSessionExpiredModalOk = () => {
    setShowSessionExpiredModal(false)
    loginWithSSO()
  }

  // Initialize on component mount
  useEffect(() => {
    let intervalId

    const tokenRefresh = () => {
      if (expiresAt) {
        const now = new Date()
        const expirationDate = new Date(expiresAt)
        const timeRemaining = expirationDate - now

        if (timeRemaining <= 0) {
          if (authenticated == true) {
            setAuthenticated(false)
          }
          setShowSessionExpiredModal(true)
          notificationApi.destroy('token-expiration')
        } else {
          if (authenticated == false) {
            setAuthenticated(true)
          }

          const minutes = Math.floor(timeRemaining / 60000)
          const seconds = Math.floor((timeRemaining % 60000) / 1000)

          // Only show notification in the final minute
          if (minutes === 0) {
            const totalSeconds = 60
            const remainingSeconds = totalSeconds - seconds
            const progress = (remainingSeconds / totalSeconds) * 100

            notificationApi.info({
              message: 'Session Expiring Soon',
              description: (
                <div>
                  <div style={{ marginBottom: 8 }}>
                    Your session will expire in {seconds} seconds
                  </div>
                  <Progress
                    percent={progress}
                    size='small'
                    status='active'
                    showInfo={false}
                  />
                </div>
              ),
              duration: 0,
              key: 'token-expiration',
              icon: null,
              placement: 'bottomRight',
              style: {
                width: 360
              },
              className: 'token-expiration-notification',
              closeIcon: null,
              onClose: () => {},
              btn: (
                <Button
                  type='primary'
                  size='small'
                  onClick={() => {
                    notificationApi.destroy('token-expiration')
                    refreshToken()
                  }}
                >
                  Reload Session
                </Button>
              )
            })
          } else if (minutes === 1) {
            // Clear any existing notification when we enter the final minute
            notificationApi.destroy('token-expiration')
          }
        }
      } else {
        // Check cookies directly if expiresAt is not set in state
        const expiryInfo = checkAuthCookiesExpiry(5) // Check if expiring within 5 minutes
        if (expiryInfo.isExpiringSoon && expiryInfo.minutesRemaining <= 1) {
          // Show notification for cookies expiring soon
          const seconds = Math.floor((expiryInfo.timeRemaining % 60000) / 1000)
          const totalSeconds = 60
          const remainingSeconds = totalSeconds - seconds
          const progress = (remainingSeconds / totalSeconds) * 100

          notificationApi.info({
            message: 'Session Expiring Soon',
            description: (
              <div>
                <div style={{ marginBottom: 8 }}>
                  Your session will expire in {seconds} seconds
                </div>
                <Progress
                  percent={progress}
                  size='small'
                  status='active'
                  showInfo={false}
                />
              </div>
            ),
            duration: 0,
            key: 'token-expiration',
            icon: null,
            placement: 'bottomRight',
            style: {
              width: 360
            },
            className: 'token-expiration-notification',
            closeIcon: null,
            onClose: () => {},
            btn: (
              <Button
                type='primary'
                size='small'
                onClick={() => {
                  notificationApi.destroy('token-expiration')
                  refreshToken()
                }}
              >
                Reload Session
              </Button>
            )
          })
        }
      }
    }

    intervalId = setInterval(tokenRefresh, 1000)
    tokenRefresh()

    return () => {
      if (intervalId) {
        clearInterval(intervalId)
      }
    }
  }, [expiresAt, authenticated, notificationApi, refreshToken])

  useEffect(() => {
    const authCode =
      new URLSearchParams(location.search).get('authCode') || null
    if (authCode != null) {
      getLoginToken(authCode)
    } else if (
      token == null &&
      retreivedTokenFromCookies == true &&
      initialized == false &&
      authCode == null
    ) {
      setInitialized(true)
      console.log('Showing unauth', token, authCode)
      setShowUnauthorizedModal(true)
      setAuthenticated(false)
    }
  }, [
    checkAuthStatus,
    location.search,
    getLoginToken,
    initialized,
    location.pathname,
    navigate,
    token,
    retreivedTokenFromCookies
  ])

  return (
    <>
      {contextHolder}
      {notificationContextHolder}
      <AuthContext.Provider
        value={{
          authenticated,
          setUnauthenticated,
          loginWithSSO,
          getLoginToken,
          token,
          loading,
          userProfile,
          logout
        }}
      >
        {children}
      </AuthContext.Provider>
      <Modal
        title={
          <Space size={'middle'}>
            <InfoCircleIcon />
            Session Expired
          </Space>
        }
        open={showSessionExpiredModal && !loading && !showAuthErrorModal}
        onOk={handleSessionExpiredModalOk}
        okText='Log In'
        style={{ maxWidth: 430 }}
        closable={false}
        centered
        maskClosable={false}
        footer={[
          <Button
            key='submit'
            type='primary'
            onClick={handleSessionExpiredModalOk}
          >
            Log In
          </Button>
        ]}
      >
        <Text>Your session has expired. Please log in again to continue.</Text>
      </Modal>
      <Modal
        title={
          <Space size={'middle'}>
            <ExclamationOctogonIcon />
            Please log in to continue
          </Space>
        }
        open={showUnauthorizedModal && !loading && !showAuthErrorModal}
        onOk={() => {
          setShowUnauthorizedModal(false)
          loginWithSSO()
        }}
        okText='Log In'
        style={{ maxWidth: 430, top: '50%', transform: 'translateY(-50%)' }}
        closable={false}
        maskClosable={false}
        footer={[
          <Button
            key='submit'
            type='primary'
            onClick={() => {
              setShowUnauthorizedModal(false)
              loginWithSSO()
            }}
          >
            Log In
          </Button>
        ]}
      >
        <Text>
          You need to be logged in to access FarmControl. Please log in with
          tombutcher.work to continue.
        </Text>
      </Modal>
      <Modal
        open={loading}
        className={'loading-modal'}
        title={false}
        height={20}
        style={{ maxWidth: 220, top: '50%', transform: 'translateY(-50%)' }}
        closable={false}
        maskClosable={false}
        footer={false}
      >
        <Space size={'middle'}>
          <LoadingOutlined />
          <Text style={{ margin: 0 }}>Loading, please wait...</Text>
        </Space>
      </Modal>
      <Modal
        title={
          <Space size={'middle'}>
            <ExclamationOctogonIcon />
            Authentication Error
          </Space>
        }
        open={showAuthErrorModal && !loading}
        style={{ maxWidth: 430 }}
        closable={false}
        centered
        maskClosable={false}
        footer={[
          <Button
            key='submit'
            onClick={() => {
              showAuthErrorModal(false)
              loginWithSSO()
            }}
          >
            Retry Login
          </Button>
        ]}
      >
        <Text>{authError}</Text>
      </Modal>
    </>
  )
}

AuthProvider.propTypes = {
  children: PropTypes.node.isRequired
}

export { AuthContext, AuthProvider }
