"use client"

import { createContext, useContext, useEffect, useMemo, useState } from "react"
import { customSessionClient } from "better-auth/client/plugins"
import { createAuthClient } from "better-auth/react"
import type { AppAuth } from "@/lib/auth"
import { useMutation } from "convex/react"

import { api } from "@/convex/_generated/api"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { isAdmin, isStaff } from "@/lib/authz"

export type AppSession = {
  user: {
    id: string
    name?: string | null
    email: string
    role: string
    tenantId: string | null
    avatarUrl: string | null
    machinePersona?: string | null
  }
  session: {
    id: string
    expiresAt: number
  }
}

type MachineContext = {
  machineId: string
  tenantId: string
  persona: string | null
  assignedUserId: string | null
  assignedUserEmail: string | null
  assignedUserName: string | null
  assignedUserRole: string | null
  companyId: string | null
  isActive: boolean
}

type MachineContextError = {
  status: number
  message: string
  details?: Record<string, unknown> | null
}

declare global {
  interface Window {
    __machineContextDebug?: unknown
  }
}

const authClient = createAuthClient({
  plugins: [customSessionClient<AppAuth>()],
  fetchOptions: {
    credentials: "include",
  },
})

type AuthContextValue = {
  session: AppSession | null
  isLoading: boolean
  convexUserId: string | null
  role: string | null
  isAdmin: boolean
  isStaff: boolean
  isCustomer: boolean
  machineContext: MachineContext | null
  machineContextLoading: boolean
  machineContextError: MachineContextError | null
}

const AuthContext = createContext<AuthContextValue>({
  session: null,
  isLoading: true,
  convexUserId: null,
  role: null,
  isAdmin: false,
  isStaff: false,
  isCustomer: false,
  machineContext: null,
  machineContextLoading: false,
  machineContextError: null,
})

export function useAuth() {
  return useContext(AuthContext)
}

export const { signIn, signOut, useSession } = authClient

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const { data: session, isPending } = useSession()
  const ensureUser = useMutation(api.users.ensureUser)
  const [convexUserId, setConvexUserId] = useState<string | null>(null)
  const [machineContext, setMachineContext] = useState<MachineContext | null>(null)
  const [machineContextLoading, setMachineContextLoading] = useState(false)
  const [machineContextError, setMachineContextError] = useState<MachineContextError | null>(null)

  useEffect(() => {
    if (!session?.user || session.user.role === "machine") {
      setConvexUserId(null)
    }
  }, [session?.user])

  // Sempre tenta obter o contexto da máquina.
  // 1) Se a sessão Better Auth indicar role "machine", buscamos normalmente.
  // 2) Se a sessão vier nula (alguns ambientes WebView), ainda assim tentamos
  //    carregar o contexto — se a API responder 200, assumimos que há sessão válida
  //    do lado do servidor e populamos o contexto para o restante do app.
  useEffect(() => {
    if (isPending) {
      setMachineContextLoading(true)
      return
    }

    const shouldFetch = Boolean(session?.user?.role === "machine") || !session?.user
    if (!shouldFetch) {
      setMachineContext(null)
      setMachineContextError(null)
      setMachineContextLoading(false)
      return
    }

    let cancelled = false
    setMachineContextLoading(true)
    setMachineContextError(null)
    ;(async () => {
      try {
        const response = await fetch("/api/machines/session", { credentials: "include" })
        if (!response.ok) {
          let payload: Record<string, unknown> | null = null
          try {
            const parsed = await response.clone().json()
            if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
              payload = parsed as Record<string, unknown>
            }
          } catch {
            payload = null
          }
          const fallbackMessage = "Falha ao carregar o contexto da maquina."
          const message =
            (payload && typeof payload.error === "string" && payload.error.trim()) || fallbackMessage
          if (!cancelled) {
            // For 401/403/404 we silently ignore: most browsers hitting the
            // web app are not machines and won't have a machine session.
            if (response.status !== 401 && response.status !== 403 && response.status !== 404) {
              const debugPayload = {
                status: response.status,
                message,
                payload,
                timestamp: new Date().toISOString(),
              }
              // Use warn to reduce noise in production consoles
              console.warn("[auth] machine context request failed", debugPayload)
              if (typeof window !== "undefined") {
                window.__machineContextDebug = debugPayload
              }
            }
            setMachineContext(null)
            setMachineContextError({
              status: response.status,
              message,
              details: payload,
            })
          }
          return
        }
        const data = await response.json()
        if (!cancelled) {
          const machine = data.machine as {
            id: string
            tenantId: string
            persona: string | null
            assignedUserId: string | null
            assignedUserEmail: string | null
            assignedUserName: string | null
            assignedUserRole: string | null
            companyId: string | null
            isActive?: boolean
          }
          setMachineContext({
            machineId: machine.id,
            tenantId: machine.tenantId,
            persona: machine.persona ?? null,
            assignedUserId: machine.assignedUserId ?? null,
            assignedUserEmail: machine.assignedUserEmail ?? null,
            assignedUserName: machine.assignedUserName ?? null,
            assignedUserRole: machine.assignedUserRole ?? null,
            companyId: machine.companyId ?? null,
            isActive: machine.isActive ?? true,
          })
          setMachineContextError(null)
        }
      } catch (error) {
        console.error("Failed to load machine context", error)
        if (!cancelled) {
          const debugPayload = {
            error: error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : String(error),
            timestamp: new Date().toISOString(),
          }
          console.error("[auth] machine context unexpected error", debugPayload)
          if (typeof window !== "undefined") {
            window.__machineContextDebug = debugPayload
          }
          setMachineContext(null)
          setMachineContextError({
            status: 0,
            message: "Erro ao carregar o contexto da maquina.",
            details: error instanceof Error ? { message: error.message } : null,
          })
        }
      } finally {
        if (!cancelled) {
          setMachineContextLoading(false)
        }
      }
    })()

    return () => {
      cancelled = true
    }
  }, [session?.user, isPending])

  // Poll machine session periodically to reflect admin changes (e.g., deactivation)
  useEffect(() => {
    const shouldPoll = Boolean(session?.user?.role === "machine") || Boolean(machineContext)
    if (!shouldPoll) return
    let cancelled = false
    const tick = async () => {
      try {
        const response = await fetch("/api/machines/session", { credentials: "include" })
        if (!response.ok) return
        const data = await response.json()
        if (cancelled) return
        const mc = data?.machine
        if (mc && typeof mc === "object") {
          setMachineContext((prev) => {
            // only update when something changes to avoid re-renders
            const next = {
              machineId: mc.id,
              tenantId: mc.tenantId,
              persona: mc.persona ?? null,
              assignedUserId: mc.assignedUserId ?? null,
              assignedUserEmail: mc.assignedUserEmail ?? null,
              assignedUserName: mc.assignedUserName ?? null,
              assignedUserRole: mc.assignedUserRole ?? null,
              companyId: mc.companyId ?? null,
              isActive: (mc.isActive ?? true) as boolean,
            } as MachineContext
            return JSON.stringify(prev) === JSON.stringify(next) ? prev : next
          })
        }
      } catch {
        /* ignore transient errors */
      }
    }
    const id = setInterval(tick, 15000)
    // Also refresh when tab gains focus
    const onFocus = () => tick()
    if (typeof window !== "undefined") {
      window.addEventListener("focus", onFocus)
      document.addEventListener("visibilitychange", onFocus)
    }
    tick()
    return () => {
      cancelled = true
      clearInterval(id)
      if (typeof window !== "undefined") {
        window.removeEventListener("focus", onFocus)
        document.removeEventListener("visibilitychange", onFocus)
      }
    }
  }, [session?.user?.role, machineContext])

  useEffect(() => {
    if (!session?.user || session.user.role === "machine" || convexUserId) return

    const controller = new AbortController()

    ;(async () => {
      try {
        const ensured = await ensureUser({
          tenantId: session.user.tenantId ?? DEFAULT_TENANT_ID,
          name: session.user.name ?? session.user.email,
          email: session.user.email,
          avatarUrl: session.user.avatarUrl ?? undefined,
          role: session.user.role.toUpperCase(),
        })
        if (!controller.signal.aborted) {
          setConvexUserId(ensured?._id ?? null)
        }
      } catch (error) {
        if (!controller.signal.aborted) {
          console.error("Failed to sync user with Convex", error)
        }
      }
    })()

    return () => {
      controller.abort()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [ensureUser, session?.user?.email, session?.user?.tenantId, session?.user?.role, convexUserId])

  // Se não houver sessão mas tivermos contexto de máquina, tratamos como "machine"
  const baseRole = session?.user?.role ? session.user.role.toLowerCase() : (machineContext ? "machine" : null)
  const personaRole = session?.user?.machinePersona ? session.user.machinePersona.toLowerCase() : null
  const normalizedRole =
    baseRole === "machine" ? (machineContext?.persona ?? personaRole ?? null) : baseRole

  const effectiveConvexUserId = baseRole === "machine" ? (machineContext?.assignedUserId ?? null) : convexUserId

  const value = useMemo<AuthContextValue>(
    () => ({
      session: session ?? null,
      isLoading: isPending,
      convexUserId: effectiveConvexUserId,
      role: normalizedRole,
      isAdmin: isAdmin(normalizedRole),
      isStaff: isStaff(normalizedRole),
      isCustomer: normalizedRole === "collaborator",
      machineContext,
      machineContextLoading,
      machineContextError,
    }),
    [session, isPending, effectiveConvexUserId, normalizedRole, machineContext, machineContextLoading, machineContextError]
  )

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
