import type { H3Event } from 'h3'

// Cache for org-role access mapping
let orgRoleAccessCache: Map<number, Set<number>> | null = null
let cacheTimestamp: number = 0
const CACHE_DURATION = 5 * 60 * 1000 // 5 minutes

interface OrgRecord {
  id: number
  AD_Role_OrgAccess?: Array<{
    AD_Role_ID?: {
      id: number
    }
    IsActive?: boolean
  }>
}

/**
 * Fetches and caches organization-role access mapping from iDempiere
 * Returns a Map where:
 * - Key: Organization ID
 * - Value: Set of Role IDs that have access to this organization
 *
 * Special case: Organization ID 0 (Name: "*") means "All Organizations"
 */
export async function getOrgRoleAccessMap(event: H3Event, authToken?: string | null, skipTokenRefresh?: boolean): Promise<Map<number, Set<number>>> {
  const now = Date.now()

  // Return cached data if still valid
  if (orgRoleAccessCache && (now - cacheTimestamp) < CACHE_DURATION) {
    return orgRoleAccessCache
  }

  const fetchOrgAccess = async (token: string) => {
    const res: any = await event.context.fetch(
      'models/ad_org?$expand=AD_Role_OrgAccess',
      'GET',
      token,
      null
    )

    if (!res?.records) {
      console.error('[OrgRoleAccessCache] Failed to fetch org-role access data')
      return null
    }

    // Build the mapping
    const accessMap = new Map<number, Set<number>>()

    for (const org of res.records as OrgRecord[]) {
      const orgId = org.id
      const roleSet = new Set<number>()

      if (org.AD_Role_OrgAccess && Array.isArray(org.AD_Role_OrgAccess)) {
        for (const access of org.AD_Role_OrgAccess) {
          if (access.IsActive !== false && access.AD_Role_ID?.id) {
            roleSet.add(access.AD_Role_ID.id)
          }
        }
      }

      accessMap.set(orgId, roleSet)
    }

    return accessMap
  }

  try {
    let token = authToken || await getTokenHelper(event)
    let accessMap: Map<number, Set<number>> | null = null

    try {
      accessMap = await fetchOrgAccess(token)
    } catch (fetchError: any) {
      // On 401, try refreshing the token and retry once
      // Skip refresh if called from SSE context (headers already sent)
      if (!skipTokenRefresh && (fetchError?.statusCode === 401 || fetchError?.status === 401)) {
        try {
          const refreshedToken = await refreshTokenHelper(event)
          accessMap = await fetchOrgAccess(refreshedToken)
        } catch (refreshError: any) {
          console.error('[OrgRoleAccessCache] Token refresh also failed:', refreshError.message)
        }
      } else if (fetchError?.statusCode === 401 || fetchError?.status === 401) {
        console.error('[OrgRoleAccessCache] Token expired (refresh skipped for SSE context)')
      } else {
        throw fetchError
      }
    }

    if (!accessMap) {
      return orgRoleAccessCache || new Map()
    }

    // Cache the result
    orgRoleAccessCache = accessMap
    cacheTimestamp = now

    console.log(`[OrgRoleAccessCache] Loaded ${accessMap.size} organizations with role mappings`)

    return accessMap
  } catch (error: any) {
    console.error('[OrgRoleAccessCache] Error fetching org-role access:', error.message)
    // Return existing cache or empty map on error
    return orgRoleAccessCache || new Map()
  }
}

/**
 * Checks if a given role has access to a specific organization
 * Handles the special case where org ID 0 (Name: "*") means "All Organizations"
 */
export function hasRoleAccessToOrg(
  orgRoleAccessMap: Map<number, Set<number>>,
  roleId: number,
  orgId: number
): boolean {
  // Check if role has access to org ID 0 ("*" - All Organizations)
  const allOrgsRoles = orgRoleAccessMap.get(0)
  if (allOrgsRoles && allOrgsRoles.has(roleId)) {
    return true // Role has access to all organizations
  }

  // Check if role has access to the specific organization
  const orgRoles = orgRoleAccessMap.get(orgId)
  return orgRoles ? orgRoles.has(roleId) : false
}

/**
 * Invalidates the cache, forcing a refresh on next access
 */
export function invalidateOrgRoleAccessCache() {
  orgRoleAccessCache = null
  cacheTimestamp = 0
  console.log('[OrgRoleAccessCache] Cache invalidated')
}
