import { prisma } from '@/lib/db'
import { auditLog } from '@/lib/audit'
import { getGameDbPool, getGameDbConfig } from '@/lib/game-db'
import type { RowDataPacket } from 'mysql2'

// ==========================================
// IDENTIFIER MAPPING SERVICE
// ==========================================
// Handles the mapping between Discord accounts and FiveM player identifiers
// This is the core of the first-join access system

export interface PlayerIdentifiers {
  discord: string
  license?: string
  steam?: string
  fivem?: string
  xbl?: string
  live?: string
  ip?: string
}

export interface MappingResult {
  success: boolean
  created: boolean
  mapping?: {
    id: string
    discordId: string
    license: string | null
    steam: string | null
    gameIdentifier: string | null
    characterId: string | null
    firstJoinAt: Date
    lastSeenAt: Date
  }
  error?: string
}

/**
 * Create or update an identifier mapping when a player joins the FiveM server
 * This is called from the FiveM integration script
 */
export async function createOrUpdateMapping(
  identifiers: PlayerIdentifiers,
  gameIdentifier?: string,
  characterId?: string
): Promise<MappingResult> {
  try {
    // Validate Discord ID
    if (!identifiers.discord) {
      return {
        success: false,
        created: false,
        error: 'Discord identifier is required',
      }
    }

    // Normalize Discord ID (remove "discord:" prefix if present)
    const discordId = identifiers.discord.replace('discord:', '')

    // Check if mapping already exists
    const existingMapping = await prisma.identifierMapping.findUnique({
      where: { discordId },
    })

    if (existingMapping) {
      // Update existing mapping
      const updated = await prisma.identifierMapping.update({
        where: { discordId },
        data: {
          license: identifiers.license || existingMapping.license,
          steam: identifiers.steam || existingMapping.steam,
          fivem: identifiers.fivem || existingMapping.fivem,
          xbl: identifiers.xbl || existingMapping.xbl,
          live: identifiers.live || existingMapping.live,
          gameIdentifier: gameIdentifier || existingMapping.gameIdentifier,
          characterId: characterId || existingMapping.characterId,
          lastSeenAt: new Date(),
        },
      })

      await auditLog({
        action: 'IDENTIFIER_MAPPING_UPDATED',
        category: 'AUTH',
        details: {
          discordId,
          hadExisting: true,
        },
      })

      return {
        success: true,
        created: false,
        mapping: updated,
      }
    }

    // Create new mapping
    const newMapping = await prisma.identifierMapping.create({
      data: {
        discordId,
        license: identifiers.license,
        steam: identifiers.steam,
        fivem: identifiers.fivem,
        xbl: identifiers.xbl,
        live: identifiers.live,
        gameIdentifier,
        characterId,
        firstJoinAt: new Date(),
        lastSeenAt: new Date(),
      },
    })

    await auditLog({
      action: 'IDENTIFIER_MAPPING_CREATED',
      category: 'AUTH',
      details: {
        discordId,
        hasLicense: !!identifiers.license,
        hasSteam: !!identifiers.steam,
        hasGameIdentifier: !!gameIdentifier,
      },
    })

    return {
      success: true,
      created: true,
      mapping: newMapping,
    }
  } catch (error) {
    console.error('[IdentifierMapping] Error creating/updating mapping:', error)
    
    await auditLog({
      action: 'IDENTIFIER_MAPPING_FAILED',
      category: 'AUTH',
      details: {
        discordId: identifiers.discord,
        error: error instanceof Error ? error.message : 'Unknown error',
      },
    })

    return {
      success: false,
      created: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    }
  }
}

/**
 * Get mapping by Discord ID
 * First checks UCP database, then falls back to checking the game database directly
 */
export async function getMappingByDiscordId(discordId: string) {
  try {
    const normalizedId = discordId.replace('discord:', '')
    
    // First, check UCP database for existing mapping
    const ucpMapping = await prisma.identifierMapping.findUnique({
      where: { discordId: normalizedId },
    })
    
    if (ucpMapping) {
      return ucpMapping
    }
    
    // If no UCP mapping, check if the discord ID exists in the game database
    // This handles the case where the FiveM script writes directly to players table
    const gameDbPlayer = await checkGameDatabaseForDiscord(normalizedId)
    
    if (gameDbPlayer) {
      // Auto-create the UCP mapping from game database data
      const newMapping = await prisma.identifierMapping.create({
        data: {
          discordId: normalizedId,
          gameIdentifier: gameDbPlayer.citizenid || gameDbPlayer.identifier,
          characterId: gameDbPlayer.citizenid || null,
          license: gameDbPlayer.license || null,
          firstJoinAt: new Date(),
          lastSeenAt: new Date(),
          isVerified: true,
        },
      })
      
      await auditLog({
        action: 'IDENTIFIER_MAPPING_AUTO_CREATED',
        category: 'AUTH',
        details: {
          discordId: normalizedId,
          source: 'game_database',
          citizenid: gameDbPlayer.citizenid,
        },
      })
      
      return newMapping
    }
    
    return null
  } catch (error) {
    console.error('[IdentifierMapping] Error getting mapping:', error)
    return null
  }
}

/**
 * Check the game database directly for a player with the given Discord ID
 */
async function checkGameDatabaseForDiscord(discordId: string): Promise<{
  citizenid?: string
  identifier?: string
  license?: string
} | null> {
  try {
    const config = await getGameDbConfig()
    if (!config) return null
    
    const pool = await getGameDbPool()
    if (!pool) return null
    
    if (config.framework === 'QBCORE') {
      // QBCore: Check players table for discord column
      const [rows] = await pool.query<RowDataPacket[]>(
        `SELECT citizenid, license FROM players WHERE discord = ? LIMIT 1`,
        [discordId]
      )
      
      if (rows && rows.length > 0) {
        return {
          citizenid: rows[0].citizenid,
          license: rows[0].license,
        }
      }
    } else {
      // ESX: Check users table
      const [rows] = await pool.query<RowDataPacket[]>(
        `SELECT identifier FROM users WHERE identifier LIKE ? LIMIT 1`,
        [`discord:${discordId}`]
      )
      
      if (rows && rows.length > 0) {
        return {
          identifier: rows[0].identifier,
        }
      }
    }
    
    return null
  } catch (error) {
    console.error('[IdentifierMapping] Error checking game database:', error)
    return null
  }
}

/**
 * Check if a Discord user has completed their first join
 */
export async function hasCompletedFirstJoin(discordId: string): Promise<boolean> {
  const mapping = await getMappingByDiscordId(discordId)
  return mapping !== null && mapping.isVerified
}

/**
 * Get all mappings (admin function)
 */
export async function getAllMappings(options?: {
  limit?: number
  offset?: number
  search?: string
}) {
  const { limit = 50, offset = 0, search } = options || {}

  const where = search
    ? {
        OR: [
          { discordId: { contains: search } },
          { license: { contains: search } },
          { steam: { contains: search } },
          { gameIdentifier: { contains: search } },
        ],
      }
    : {}

  const [mappings, total] = await Promise.all([
    prisma.identifierMapping.findMany({
      where,
      orderBy: { lastSeenAt: 'desc' },
      take: limit,
      skip: offset,
    }),
    prisma.identifierMapping.count({ where }),
  ])

  return { mappings, total }
}

/**
 * Delete a mapping (admin function)
 */
export async function deleteMapping(discordId: string): Promise<boolean> {
  try {
    await prisma.identifierMapping.delete({
      where: { discordId },
    })

    await auditLog({
      action: 'IDENTIFIER_MAPPING_DELETED',
      category: 'ADMIN',
      details: { discordId },
    })

    return true
  } catch {
    return false
  }
}

/**
 * Get mapping statistics
 */
export async function getMappingStats() {
  const [total, recentJoins, withSteam, withLicense] = await Promise.all([
    prisma.identifierMapping.count(),
    prisma.identifierMapping.count({
      where: {
        firstJoinAt: {
          gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
        },
      },
    }),
    prisma.identifierMapping.count({
      where: { steam: { not: null } },
    }),
    prisma.identifierMapping.count({
      where: { license: { not: null } },
    }),
  ])

  return {
    total,
    recentJoins,
    withSteam,
    withLicense,
  }
}
