import LogtoClient, { CookieStorage } from '@logto/node'
import { getRequestURL, getCookie, setCookie, sendRedirect, type H3Event } from 'h3'
import { randomUUID } from 'crypto'

const SESSION_COOKIE_NAME = 'logtoSession'
const LEGACY_COOKIE_NAME = 'logtoCookies'

const SCOPES = [
  'openid',
  'profile',
  'email',
  'offline_access',
  'urn:logto:scope:organizations',
  'urn:logto:scope:organization_token',
  'teams:member'
]

const RESOURCES = [
  'https://teams.optovia.ru',
  'https://orders.optovia.ru',
  'https://kyc.optovia.ru',
  'https://exchange.optovia.ru',
  'https://billing.optovia.ru'
]

const createSessionWrapper = (event: H3Event) => {
  const storage = useStorage('logto')
  let currentSessionId = ''

  return {
    wrap: async (data: Record<string, unknown>) => {
      if (!data || Object.keys(data).length === 0) {
        if (currentSessionId) {
          await storage.removeItem(`session:${currentSessionId}`)
        }
        currentSessionId = ''
        return ''
      }

      if (!currentSessionId) {
        currentSessionId = randomUUID()
      }

      await storage.setItem(`session:${currentSessionId}`, data)
      return currentSessionId
    },
    unwrap: async (value: string) => {
      currentSessionId = value || ''
      if (!currentSessionId) return {}
      const stored = await storage.getItem<Record<string, unknown>>(`session:${currentSessionId}`)
      if (!stored) {
        // Session ID exists in cookie but data not found in storage - clear the stale cookie
        setCookie(event, SESSION_COOKIE_NAME, '', { path: '/', maxAge: 0 })
        currentSessionId = ''
        return {}
      }
      return stored
    }
  }
}

export default defineEventHandler(async (event) => {
  const endpoint = process.env.NUXT_LOGTO_ENDPOINT || 'https://auth.optovia.ru'
  const appId = process.env.NUXT_LOGTO_APP_ID || ''
  const appSecret = process.env.NUXT_LOGTO_APP_SECRET || ''

  if (!appId || !appSecret) {
    return
  }

  const url = getRequestURL(event)

  if (getCookie(event, LEGACY_COOKIE_NAME)) {
    setCookie(event, LEGACY_COOKIE_NAME, '', { path: '/', maxAge: 0 })
  }

  // Check for stale session cookie BEFORE initializing CookieStorage
  const existingSessionId = getCookie(event, SESSION_COOKIE_NAME)
  if (existingSessionId) {
    const nitroStorage = useStorage('logto')
    const sessionData = await nitroStorage.getItem(`session:${existingSessionId}`)
    if (!sessionData) {
      // Session cookie exists but no data in storage - clear it
      setCookie(event, SESSION_COOKIE_NAME, '', { path: '/', maxAge: 0 })
    }
  }

  const storage = new CookieStorage({
    cookieKey: SESSION_COOKIE_NAME,
    isSecure: process.env.NODE_ENV === 'production',
    sessionWrapper: createSessionWrapper(event),
    getCookie: async (name) => getCookie(event, name),
    setCookie: async (name, value, options) => {
      setCookie(event, name, value, options)
    }
  })

  await storage.init()

  const logto = new LogtoClient(
    {
      endpoint,
      appId,
      appSecret,
      resources: RESOURCES,
      scopes: SCOPES
    },
    {
      navigate: async (target) => {
        await sendRedirect(event, target, 302)
      },
      storage
    }
  )

  if (url.pathname === '/sign-in') {
    await logto.signIn({
      redirectUri: new URL('/callback', url).href
    })
    return
  }

  if (url.pathname === '/sign-out') {
    await logto.signOut(new URL('/', url).href)
    return
  }

  if (url.pathname === '/callback') {
    await logto.handleSignInCallback(url.href)
    await sendRedirect(event, '/', 302)
    return
  }

  event.context.logtoClient = logto

  if (await logto.isAuthenticated()) {
    try {
      event.context.logtoUser = await logto.fetchUserInfo()
    } catch {
      try {
        event.context.logtoUser = await logto.getIdTokenClaims()
      } catch {
        event.context.logtoUser = undefined
      }
    }
  }

  const orgId = (event.context.logtoUser as { organizations?: string[] } | undefined)?.organizations?.[0]
  if (orgId) {
    event.context.logtoOrgId = orgId
  }
})
