// src/contexts/AuthContext.js
import React, {
  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 AppError from '../../App/AppError'
import loglevel from 'loglevel'
import { ElectronContext } from './ElectronContext'
import { useLocation, useNavigate } from 'react-router-dom'
const logger = loglevel.getLogger('ApiServerContext')
logger.setLevel(config.logLevel)

const AuthContext = createContext()

const Title = 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 [retreivedTokenFromSession, setRetreivedTokenFromSession] =
    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 [authError, setAuthError] = useState(null)
  const { openExternalUrl, isElectron } = useContext(ElectronContext)
  const location = useLocation()
  const navigate = useNavigate()

  // Read token from session storage if present
  useEffect(() => {
    const storedToken = sessionStorage.getItem('authToken')
    const storedExpiresAt = sessionStorage.getItem('authExpiresAt')
    if (storedToken && storedExpiresAt) {
      setToken(storedToken)
      setExpiresAt(storedExpiresAt)
      setAuthenticated(true)
    } else {
      setAuthenticated(false)
      setShowUnauthorizedModal(true)
    }
    setRetreivedTokenFromSession(true)
  }, [])

  const logout = useCallback((redirectUri = '/login') => {
    setAuthenticated(false)
    setToken(null)
    setExpiresAt(null)
    setUserProfile(null)
    sessionStorage.removeItem('authToken')
    sessionStorage.removeItem('authExpiresAt')
    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/${isElectron ? 'app/' : ''}login?redirect_uri=${encodeURIComponent(redirectUri)}`
      if (isElectron) {
        console.log('Opening external url...')
        openExternalUrl(loginUrl)
        setLoading(true)
      } else {
        console.log('Redirecting...')
        window.location.href = loginUrl
      }
    },
    [messageApi, openExternalUrl, isElectron, location.search]
  )

  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/${isElectron ? 'app/' : ''}token?code=${code}`
        )

        if (response.status === 200 && response.data) {
          logger.debug('Got auth token!')
          setToken(response.data.access_token)
          setExpiresAt(response.data.expires_at)
          setUserProfile(response.data)
          sessionStorage.setItem('authToken', response.data.access_token)
          sessionStorage.setItem('authExpiresAt', response.data.expires_at)
          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.')
        }
      } catch (error) {
        logger.debug('Auth check failed', error)
        if (error.response?.status === 401) {
          setShowUnauthorizedModal(true)
        } else {
          setAuthError('Error connecting to authentication service.')
        }
        setAuthenticated(false)
      } finally {
        setLoading(false)
      }
    },
    [isElectron]
  )
  // 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!')
        setToken(response.data.access_token)
        setExpiresAt(response.data.expires_at)
        setUserProfile(response.data)
        sessionStorage.setItem('authToken', response.data.access_token)
        sessionStorage.setItem('authExpiresAt', response.data.expires_at)
      } else {
        setAuthenticated(false)
        setAuthError('Failed to authenticate user.')
      }
    } catch (error) {
      logger.debug('Auth check failed', error)
      if (error.response?.status === 401) {
        setShowUnauthorizedModal(true)
      } else {
        setAuthError('Error connecting to authentication service.')
      }
      setAuthenticated(false)
    } finally {
      setLoading(false)
    }
  }, [token])

  const setUnauthenticated = () => {
    setAuthenticated(false)
    setShowUnauthorizedModal(true)
  }

  const refreshToken = useCallback(async () => {
    try {
      const response = await axios.get(`${config.backendUrl}/auth/refresh`, {
        withCredentials: true
      })
      if (response.status === 200 && response.data) {
        setToken(response.data.access_token)
        setExpiresAt(response.data.expires_at)
        sessionStorage.setItem('authToken', response.data.access_token)
        sessionStorage.setItem('authExpiresAt', response.data.expires_at)
      }
    } catch (error) {
      console.error('Token refresh failed', error)
    }
  }, [])

  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')
          }
        }
      }
    }

    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 &&
      retreivedTokenFromSession == true &&
      initialized == false &&
      authCode == null
    ) {
      setInitialized(true)
      console.log('Showing unauth')
      setShowUnauthorizedModal(true)
      setAuthenticated(false)
    }
  }, [
    checkAuthStatus,
    location.search,
    getLoginToken,
    initialized,
    location.pathname,
    navigate,
    token,
    retreivedTokenFromSession
  ])

  if (authError) {
    return <AppError message={authError} showBack={false} />
  }

  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}
        onOk={handleSessionExpiredModalOk}
        okText='Log In'
        style={{ maxWidth: 430 }}
        closable={false}
        centered
        maskClosable={false}
        footer={[
          <Button
            key='submit'
            type='primary'
            onClick={handleSessionExpiredModalOk}
          >
            Log In
          </Button>
        ]}
      >
        Your session has expired. Please log in again to continue.
      </Modal>
      <Modal
        title={
          <Space size={'middle'}>
            <ExclamationOctogonIcon />
            Please log in to continue
          </Space>
        }
        open={showUnauthorizedModal}
        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>
        ]}
      >
        You need to be logged in to access FarmControl. Please log in with
        tombutcher.work to continue.
      </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 />
          <Title level={5} style={{ margin: 0 }}>
            Loading, please wait...
          </Title>
        </Space>
      </Modal>
    </>
  )
}

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

export { AuthContext, AuthProvider }
