/**
 * Role Management Utilities
 *
 * Helper functions for managing user roles and product visibility.
 * MVP: These functions work with manually assigned roles (status='approved')
 * Phase 2/3: Will be extended to handle role request/approval workflow
 */

import { and, eq, inArray } from 'drizzle-orm'
import type { SQL } from 'drizzle-orm'
import { roles, userRoles, productRoleVisibility } from '../database/schema'

/**
 * Get all approved roles for a user
 * MVP: Returns roles with status='approved' (manually assigned via DB)
 * Phase 2/3: Used to check which products a user can see
 *
 * @param userId - The user's UUID
 * @returns Array of role objects with full details
 */
export async function getUserApprovedRoles(userId: string) {
  const db = useDatabase()

  const approvedRoles = await db.query.userRoles.findMany({
    where: and(eq(userRoles.userId, userId), eq(userRoles.status, 'approved')),
    with: {
      role: true,
    },
  })

  return approvedRoles.map((ur) => ur.role)
}

/**
 * Get approved role codes for a user (lightweight version)
 * Returns just the role codes ['private', 'educator'] instead of full objects
 *
 * @param userId - The user's UUID
 * @returns Array of role codes
 */
export async function getUserApprovedRoleCodes(userId: string): Promise<string[]> {
  const approvedRoles = await getUserApprovedRoles(userId)
  return approvedRoles.map((role) => role.code)
}

/**
 * Check if a specific product is visible to a user based on their roles
 *
 * Logic:
 * - If product has NO role assignments → NOT visible (opt-in visibility)
 * - If product HAS role assignments → visible only if user has at least one matching role
 *
 * @param productId - The product's UUID
 * @param userId - The user's UUID
 * @returns true if user can see the product, false otherwise
 */
export async function isProductVisibleForUser(
  productId: string,
  userId: string
): Promise<boolean> {
  const db = useDatabase()

  // Get user's approved role codes
  const userRoleCodes = await getUserApprovedRoleCodes(userId)

  // Get product's role visibility assignments
  const productRoles = await db.query.productRoleVisibility.findMany({
    where: eq(productRoleVisibility.productId, productId),
    with: {
      role: true,
    },
  })

  // No role assignments → product is NOT visible (opt-in)
  if (productRoles.length === 0) {
    return false
  }

  // Check if user has at least one matching role
  const productRoleCodes = productRoles.map((pr) => pr.role.code)
  return productRoleCodes.some((code) => userRoleCodes.includes(code))
}

/**
 * Get all products visible to a user based on their roles
 * Returns a WHERE clause that can be used with Drizzle queries
 *
 * Usage:
 * ```typescript
 * const visibleProductIds = await getVisibleProductIdsForUser(userId)
 * const products = await db.query.products.findMany({
 *   where: inArray(products.id, visibleProductIds)
 * })
 * ```
 *
 * @param userId - The user's UUID
 * @returns Array of product UUIDs that the user can see
 */
export async function getVisibleProductIdsForUser(userId: string): Promise<string[]> {
  const db = useDatabase()

  // Get user's approved role IDs
  const userRoleRecords = await db.query.userRoles.findMany({
    where: and(eq(userRoles.userId, userId), eq(userRoles.status, 'approved')),
  })

  const userRoleIds = userRoleRecords.map((ur) => ur.roleId)

  // If user has no approved roles, they can't see any products
  if (userRoleIds.length === 0) {
    return []
  }

  // Get all products assigned to these roles
  const visibleProducts = await db.query.productRoleVisibility.findMany({
    where: inArray(productRoleVisibility.roleId, userRoleIds),
  })

  // Return unique product IDs
  return [...new Set(visibleProducts.map((pv) => pv.productId))]
}

/**
 * Assign a role to a user (MVP: manual assignment, always approved)
 * Phase 2/3: This will be replaced by role request workflow
 *
 * @param userId - The user's UUID
 * @param roleCode - Role code ('private', 'educator', 'company')
 * @param options - Optional metadata (organization name, admin notes)
 */
export async function assignRoleToUser(
  userId: string,
  roleCode: 'private' | 'educator' | 'company',
  options?: {
    organizationName?: string
    adminNotes?: string
  }
) {
  const db = useDatabase()

  // Find role by code
  const role = await db.query.roles.findFirst({
    where: eq(roles.code, roleCode),
  })

  if (!role) {
    throw new Error(`Role '${roleCode}' not found`)
  }

  // Check if user already has this role
  const existing = await db.query.userRoles.findFirst({
    where: and(eq(userRoles.userId, userId), eq(userRoles.roleId, role.id)),
  })

  if (existing) {
    throw new Error(`User already has role '${roleCode}'`)
  }

  // Create user role assignment (approved in MVP)
  const [userRole] = await db
    .insert(userRoles)
    .values({
      userId,
      roleId: role.id,
      status: 'approved', // MVP: Always approved
      organizationName: options?.organizationName,
      adminNotes: options?.adminNotes,
      statusHistory: [
        {
          status: 'approved',
          organizationName: options?.organizationName || null,
          adminNotes: options?.adminNotes || null,
          changedAt: new Date().toISOString(),
          changedBy: null, // MVP: Manual assignment (no admin tracking)
        },
      ],
    })
    .returning()

  return userRole
}

/**
 * Get role by code (helper function)
 *
 * @param roleCode - Role code ('private', 'educator', 'company')
 * @returns Role object or null if not found
 */
export async function getRoleByCode(roleCode: 'private' | 'educator' | 'company') {
  const db = useDatabase()

  return await db.query.roles.findFirst({
    where: eq(roles.code, roleCode),
  })
}

/**
 * Assign roles to a product based on category (for ERP import)
 *
 * Category Mapping:
 * - 'makerspace-annual-pass' → ['private', 'educator']
 * - 'annual-pass' → ['private']
 * - 'educator-annual-pass' → ['educator']
 * - 'company-annual-pass' → ['company']
 *
 * @param productId - The product's UUID
 * @param category - Product category from NAV ERP
 */
export async function assignRolesToProductByCategory(
  productId: string,
  category: string
) {
  const db = useDatabase()

  // Category to role mapping
  const categoryRoleMapping: Record<string, Array<'private' | 'educator' | 'company'>> = {
    'makerspace-annual-pass': ['private', 'educator'],
    'annual-pass': ['private'],
    'educator-annual-pass': ['educator'],
    'company-annual-pass': ['company'],
  }

  const roleCodes = categoryRoleMapping[category] || []

  if (roleCodes.length === 0) {
    console.warn(`No role mapping found for category '${category}'`)
    return []
  }

  // Get role IDs
  const roleRecords = await db.query.roles.findMany({
    where: inArray(roles.code, roleCodes),
  })

  const assignments = []

  // Create product-role assignments
  for (const role of roleRecords) {
    // Check if assignment already exists
    const existing = await db.query.productRoleVisibility.findFirst({
      where: and(
        eq(productRoleVisibility.productId, productId),
        eq(productRoleVisibility.roleId, role.id)
      ),
    })

    if (!existing) {
      const [assignment] = await db
        .insert(productRoleVisibility)
        .values({
          productId,
          roleId: role.id,
        })
        .returning()

      assignments.push(assignment)
    }
  }

  return assignments
}
