/**
 * Tenant-Aware Authentication Middleware
 * Enforces tenant isolation in all queries
 * Superior to Azure with more flexible permission model
 */

import { FastifyRequest, FastifyReply } from 'fastify'
import { identityService, TokenValidationResult } from '../services/identity.js'
import { getDb } from '../db/index.js'
import { logger } from '../lib/logger.js'

export interface TenantContext {
  tenantId?: string
  userId: string
  email: string
  role: string
  tenantRole?: string
  permissions: Record<string, any>
  isSystemAdmin: boolean
}

declare module 'fastify' {
  interface FastifyRequest {
    tenantContext?: TenantContext
  }
}

/**
 * Extract tenant context from request
 */
export async function extractTenantContext(
  request: FastifyRequest
): Promise<TenantContext | null> {
  // Get token from Authorization header
  const authHeader = request.headers.authorization
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return null
  }

  const token = authHeader.substring(7)

  // Validate token
  const validation = await identityService.validateToken(token)
  if (!validation.valid || !validation.userId) {
    return null
  }

  // Get user from database
  const db = getDb()
  const userResult = await db.query('SELECT id, email, role FROM users WHERE id = $1', [
    validation.userId,
  ])

  if (userResult.rows.length === 0) {
    return null
  }

  const user = userResult.rows[0]
  const isSystemAdmin = user.role === 'ADMIN'

  // Get tenant information if tenant ID is present
  let tenantRole: string | undefined
  let tenantPermissions: Record<string, any> = {}

  if (validation.tenantId) {
    const tenantUserResult = await db.query(
      `SELECT role, permissions FROM tenant_users 
       WHERE tenant_id = $1 AND user_id = $2`,
      [validation.tenantId, validation.userId]
    )

    if (tenantUserResult.rows.length > 0) {
      tenantRole = tenantUserResult.rows[0].role
      tenantPermissions = tenantUserResult.rows[0].permissions || {}
    }
  }

  return {
    tenantId: validation.tenantId,
    userId: validation.userId,
    email: validation.email || user.email,
    role: user.role,
    tenantRole,
    permissions: { ...tenantPermissions, ...(validation.permissions || {}) },
    isSystemAdmin,
  }
}

/**
 * Tenant-aware authentication middleware
 */
export async function tenantAuthMiddleware(
  request: FastifyRequest,
  reply: FastifyReply
): Promise<void> {
  // Skip auth for health check and GraphQL introspection
  if (request.url === '/health' || request.method === 'OPTIONS') {
    return
  }

  const context = await extractTenantContext(request)

  if (!context) {
    // Allow unauthenticated requests - GraphQL will handle auth per query/mutation
    return
  }

  // Attach tenant context to request
  request.tenantContext = context

  // Set tenant context in database session for RLS policies
  const db = getDb()
  if (context.userId) {
    await db.query(`SET LOCAL app.current_user_id = $1`, [context.userId])
  }
}

/**
 * Require authentication middleware
 */
export function requireAuth(
  request: FastifyRequest,
  reply: FastifyReply
): TenantContext {
  const context = request.tenantContext

  if (!context) {
    reply.code(401).send({
      error: 'Authentication required',
      code: 'UNAUTHENTICATED',
    })
    throw new Error('Authentication required')
  }

  return context
}

/**
 * Require tenant membership middleware
 */
export function requireTenant(
  request: FastifyRequest,
  reply: FastifyReply
): TenantContext {
  const context = requireAuth(request, reply)

  if (!context.tenantId && !context.isSystemAdmin) {
    reply.code(403).send({
      error: 'Tenant membership required',
      code: 'TENANT_REQUIRED',
    })
    throw new Error('Tenant membership required')
  }

  return context
}

/**
 * Require specific tenant role
 */
export function requireTenantRole(
  allowedRoles: string[]
) {
  return (request: FastifyRequest, reply: FastifyReply): TenantContext => {
    const context = requireTenant(request, reply)

    if (context.isSystemAdmin) {
      return context
    }

    if (!context.tenantRole || !allowedRoles.includes(context.tenantRole)) {
      reply.code(403).send({
        error: 'Insufficient permissions',
        code: 'FORBIDDEN',
        required: allowedRoles,
        current: context.tenantRole,
      })
      throw new Error('Insufficient tenant permissions')
    }

    return context
  }
}

/**
 * Require system admin middleware
 */
export function requireSystemAdmin(
  request: FastifyRequest,
  reply: FastifyReply
): TenantContext {
  const context = requireAuth(request, reply)

  if (!context.isSystemAdmin) {
    reply.code(403).send({
      error: 'System administrator access required',
      code: 'FORBIDDEN',
    })
    throw new Error('System administrator access required')
  }

  return context
}

/**
 * Filter resources by tenant automatically
 */
export function filterByTenant(
  query: string,
  params: any[],
  context: TenantContext
): { query: string; params: any[] } {
  // If system admin, don't filter
  if (context.isSystemAdmin) {
    return { query, params }
  }

  // If no tenant context, filter to show only system resources (tenant_id IS NULL)
  if (!context.tenantId) {
    const whereClause = query.includes('WHERE') ? 'AND tenant_id IS NULL' : 'WHERE tenant_id IS NULL'
    return {
      query: `${query} ${whereClause}`,
      params,
    }
  }

  // Filter by tenant_id
  const whereClause = query.includes('WHERE')
    ? `AND tenant_id = $${params.length + 1}`
    : `WHERE tenant_id = $${params.length + 1}`

  return {
    query: `${query} ${whereClause}`,
    params: [...params, context.tenantId],
  }
}

