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

// ==========================================
// NORMALIZED INVENTORY TYPES
// ==========================================

export interface NormalizedItem {
  name: string
  label: string
  count: number
  weight: number
  slot: number
  description?: string
  image: string
  imageUrl: string
  metadata: Record<string, unknown>
  unique: boolean
  useable: boolean
  type: 'item' | 'weapon'
}

export interface NormalizedInventory {
  items: NormalizedItem[]
  maxWeight: number
  currentWeight: number
  maxSlots: number
  usedSlots: number
  inventoryType: InventorySystemType
}

export type InventorySystemType = 'ox_inventory' | 'qb-inventory' | 'ps-inventory' | 'qs-inventory' | 'esx_inventory' | 'unknown'

// ==========================================
// INVENTORY IMAGE CONFIGURATION
// ==========================================

export interface InventoryImageConfig {
  basePath: string
  format: string
  fallbackImage: string
  linuxServerPath?: string
  publicAssetPath?: string
  perTypeMapping?: Record<string, string>
}

export async function getInventoryImageConfig(): Promise<InventoryImageConfig> {
  try {
    const settings = await prisma.systemSetting.findMany({
      where: {
        key: {
          in: [
            'inventoryImagePath',
            'inventoryImageFormat', 
            'inventoryFallbackImage',
            'inventoryLinuxPath',
            'inventoryPublicPath',
            'inventoryTypeMappings'
          ]
        }
      }
    })
    
    const settingsMap = Object.fromEntries(settings.map(s => [s.key, s.value]))
    
    let perTypeMapping: Record<string, string> = {}
    if (settingsMap.inventoryTypeMappings) {
      try {
        perTypeMapping = JSON.parse(settingsMap.inventoryTypeMappings)
      } catch {}
    }
    
    return {
      basePath: settingsMap.inventoryImagePath || '/images/items/',
      format: settingsMap.inventoryImageFormat || 'png',
      fallbackImage: settingsMap.inventoryFallbackImage || '/images/items/default.png',
      linuxServerPath: settingsMap.inventoryLinuxPath,
      publicAssetPath: settingsMap.inventoryPublicPath,
      perTypeMapping,
    }
  } catch {
    return {
      basePath: '/images/items/',
      format: 'png',
      fallbackImage: '/images/items/default.png',
    }
  }
}

export function buildItemImageUrl(itemName: string, config: InventoryImageConfig, inventoryType?: InventorySystemType): string {
  const cleanName = itemName.toLowerCase().replace(/[^a-z0-9_-]/g, '')
  
  // Check for per-type mapping first
  if (inventoryType && config.perTypeMapping?.[inventoryType]) {
    return `${config.perTypeMapping[inventoryType]}${cleanName}.${config.format}`
  }
  
  // Use public asset path if configured
  if (config.publicAssetPath) {
    return `${config.publicAssetPath}${cleanName}.${config.format}`
  }
  
  return `${config.basePath}${cleanName}.${config.format}`
}

// ==========================================
// INVENTORY SYSTEM DETECTION
// ==========================================

export async function detectInventorySystem(): Promise<InventorySystemType> {
  try {
    // Check settings first
    const setting = await prisma.systemSetting.findUnique({
      where: { key: 'inventorySystem' }
    })
    
    if (setting?.value && setting.value !== 'auto') {
      return setting.value as InventorySystemType
    }
    
    // Auto-detect by checking which tables exist
    const pool = await getGameDbPool()
    if (!pool) return 'unknown'
    
    // Check for ox_inventory tables
    try {
      const [oxRows] = await pool.query<RowDataPacket[]>(
        "SELECT 1 FROM ox_inventory LIMIT 1"
      )
      if (oxRows !== undefined) return 'ox_inventory'
    } catch {}
    
    // Check for qs_inventory tables
    try {
      const [qsRows] = await pool.query<RowDataPacket[]>(
        "SELECT 1 FROM inventory_items LIMIT 1"
      )
      if (qsRows !== undefined) return 'qs-inventory'
    } catch {}
    
    // Check for ps-inventory/lj-inventory stash tables
    try {
      const [psRows] = await pool.query<RowDataPacket[]>(
        "SELECT 1 FROM stashitems LIMIT 1"
      )
      if (psRows !== undefined) return 'ps-inventory'
    } catch {}
    
    // Default to qb-inventory if players table has inventory column
    try {
      const [qbRows] = await pool.query<RowDataPacket[]>(
        "SELECT inventory FROM players LIMIT 1"
      )
      if (qbRows !== undefined) return 'qb-inventory'
    } catch {}
    
    // Check for ESX inventory in users table
    try {
      const [esxRows] = await pool.query<RowDataPacket[]>(
        "SELECT inventory FROM users LIMIT 1"
      )
      if (esxRows !== undefined) return 'esx_inventory'
    } catch {}
    
    return 'unknown'
  } catch {
    return 'unknown'
  }
}

// ==========================================
// BASE ADAPTER INTERFACE
// ==========================================

interface InventoryAdapter {
  getCharacterInventory(characterId: string): Promise<NormalizedInventory>
  getStashInventory(stashId: string): Promise<NormalizedInventory>
  getTrunkInventory(plate: string): Promise<NormalizedInventory>
  getGloveboxInventory(plate: string): Promise<NormalizedInventory>
}

// ==========================================
// OX_INVENTORY ADAPTER
// ==========================================

async function createOxInventoryAdapter(): Promise<InventoryAdapter> {
  const config = await getInventoryImageConfig()
  
  return {
    async getCharacterInventory(characterId: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT data FROM ox_inventory WHERE owner = ? AND name = 'player'`,
          [characterId]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('ox_inventory')
        }
        
        const inventoryData = parseJson(rows[0].data)
        return parseOxInventory(inventoryData, config)
      } catch (error) {
        console.error('[OxInventory] Error:', error)
        return emptyInventory('ox_inventory')
      }
    },
    
    async getStashInventory(stashId: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT data FROM ox_inventory WHERE name = ?`,
          [stashId]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('ox_inventory')
        }
        
        const inventoryData = parseJson(rows[0].data)
        return parseOxInventory(inventoryData, config)
      } catch {
        return emptyInventory('ox_inventory')
      }
    },
    
    async getTrunkInventory(plate: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT data FROM ox_inventory WHERE name = ?`,
          [`trunk-${plate}`]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('ox_inventory')
        }
        
        const inventoryData = parseJson(rows[0].data)
        return parseOxInventory(inventoryData, config)
      } catch {
        return emptyInventory('ox_inventory')
      }
    },
    
    async getGloveboxInventory(plate: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT data FROM ox_inventory WHERE name = ?`,
          [`glovebox-${plate}`]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('ox_inventory')
        }
        
        const inventoryData = parseJson(rows[0].data)
        return parseOxInventory(inventoryData, config)
      } catch {
        return emptyInventory('ox_inventory')
      }
    },
  }
}

function parseOxInventory(data: unknown, config: InventoryImageConfig): NormalizedInventory {
  const items: NormalizedItem[] = []
  let currentWeight = 0
  
  if (Array.isArray(data)) {
    for (const item of data) {
      if (item && item.name && item.count > 0) {
        const normalizedItem: NormalizedItem = {
          name: item.name,
          label: item.label || item.name,
          count: item.count || 1,
          weight: item.weight || 0,
          slot: item.slot || items.length + 1,
          description: item.description,
          image: item.name,
          imageUrl: buildItemImageUrl(item.name, config, 'ox_inventory'),
          metadata: item.metadata || {},
          unique: item.unique || false,
          useable: true,
          type: item.name?.startsWith('WEAPON_') ? 'weapon' : 'item',
        }
        items.push(normalizedItem)
        currentWeight += (item.weight || 0) * (item.count || 1)
      }
    }
  }
  
  return {
    items,
    maxWeight: 100000,
    currentWeight,
    maxSlots: 50,
    usedSlots: items.length,
    inventoryType: 'ox_inventory',
  }
}

// ==========================================
// QB-INVENTORY ADAPTER
// ==========================================

async function createQbInventoryAdapter(): Promise<InventoryAdapter> {
  const config = await getInventoryImageConfig()
  
  return {
    async getCharacterInventory(characterId: string): Promise<NormalizedInventory> {
      try {
        console.log('[QbInventory] Querying for citizenid:', characterId)
        
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT citizenid, inventory FROM players WHERE citizenid = ?`,
          [characterId]
        )
        
        console.log('[QbInventory] Query result rows:', rows?.length || 0)
        
        if (!rows || rows.length === 0) {
          console.log('[QbInventory] No rows found for citizenid:', characterId)
          return emptyInventory('qb-inventory')
        }
        
        const inventoryData = parseJson(rows[0].inventory)
        console.log('[QbInventory] Inventory data parsed:', inventoryData ? 'success' : 'null')
        
        const result = parseQbInventory(inventoryData, config)
        console.log('[QbInventory] Items parsed:', result.items.length)
        
        return result
      } catch (error) {
        console.error('[QbInventory] Error:', error)
        return emptyInventory('qb-inventory')
      }
    },
    
    async getStashInventory(stashId: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT items FROM stashitems WHERE stash = ?`,
          [stashId]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('qb-inventory')
        }
        
        const inventoryData = parseJson(rows[0].items)
        return parseQbInventory(inventoryData, config)
      } catch {
        return emptyInventory('qb-inventory')
      }
    },
    
    async getTrunkInventory(plate: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT items FROM trunkitems WHERE plate = ?`,
          [plate]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('qb-inventory')
        }
        
        const inventoryData = parseJson(rows[0].items)
        return parseQbInventory(inventoryData, config)
      } catch {
        return emptyInventory('qb-inventory')
      }
    },
    
    async getGloveboxInventory(plate: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT items FROM gloveboxitems WHERE plate = ?`,
          [plate]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('qb-inventory')
        }
        
        const inventoryData = parseJson(rows[0].items)
        return parseQbInventory(inventoryData, config)
      } catch {
        return emptyInventory('qb-inventory')
      }
    },
  }
}

function parseQbInventory(data: unknown, config: InventoryImageConfig): NormalizedInventory {
  const items: NormalizedItem[] = []
  let currentWeight = 0
  
  if (Array.isArray(data)) {
    for (const item of data) {
      if (item && item.name && (item.amount > 0 || item.count > 0)) {
        const count = item.amount || item.count || 1
        const weight = item.weight || 0
        
        const normalizedItem: NormalizedItem = {
          name: item.name,
          label: item.label || item.name,
          count,
          weight,
          slot: item.slot || items.length + 1,
          description: item.description,
          image: item.name,
          imageUrl: buildItemImageUrl(item.name, config, 'qb-inventory'),
          metadata: item.info || item.metadata || {},
          unique: item.unique || false,
          useable: item.useable !== false,
          type: item.type === 'weapon' || item.name?.toLowerCase().includes('weapon') ? 'weapon' : 'item',
        }
        items.push(normalizedItem)
        currentWeight += weight * count
      }
    }
  } else if (data && typeof data === 'object') {
    // Object format with slot keys
    for (const [slot, item] of Object.entries(data)) {
      const itemData = item as Record<string, unknown>
      if (itemData && itemData.name && ((itemData.amount as number) > 0 || (itemData.count as number) > 0)) {
        const count = (itemData.amount as number) || (itemData.count as number) || 1
        const weight = (itemData.weight as number) || 0
        
        const normalizedItem: NormalizedItem = {
          name: itemData.name as string,
          label: (itemData.label as string) || (itemData.name as string),
          count,
          weight,
          slot: parseInt(slot) || (itemData.slot as number) || items.length + 1,
          description: itemData.description as string,
          image: itemData.name as string,
          imageUrl: buildItemImageUrl(itemData.name as string, config, 'qb-inventory'),
          metadata: (itemData.info || itemData.metadata || {}) as Record<string, unknown>,
          unique: (itemData.unique as boolean) || false,
          useable: itemData.useable !== false,
          type: itemData.type === 'weapon' ? 'weapon' : 'item',
        }
        items.push(normalizedItem)
        currentWeight += weight * count
      }
    }
  }
  
  items.sort((a, b) => a.slot - b.slot)
  
  return {
    items,
    maxWeight: 120000,
    currentWeight,
    maxSlots: 41,
    usedSlots: items.length,
    inventoryType: 'qb-inventory',
  }
}

// ==========================================
// PS-INVENTORY / LJ-INVENTORY ADAPTER
// ==========================================

async function createPsInventoryAdapter(): Promise<InventoryAdapter> {
  const config = await getInventoryImageConfig()
  
  // PS-inventory is similar to QB-inventory with some differences
  return {
    async getCharacterInventory(characterId: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT inventory FROM players WHERE citizenid = ?`,
          [characterId]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('ps-inventory')
        }
        
        const inventoryData = parseJson(rows[0].inventory)
        return parsePsInventory(inventoryData, config)
      } catch (error) {
        console.error('[PsInventory] Error:', error)
        return emptyInventory('ps-inventory')
      }
    },
    
    async getStashInventory(stashId: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT items FROM stashitems WHERE stash = ?`,
          [stashId]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('ps-inventory')
        }
        
        const inventoryData = parseJson(rows[0].items)
        return parsePsInventory(inventoryData, config)
      } catch {
        return emptyInventory('ps-inventory')
      }
    },
    
    async getTrunkInventory(plate: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT items FROM trunkitems WHERE plate = ?`,
          [plate]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('ps-inventory')
        }
        
        const inventoryData = parseJson(rows[0].items)
        return parsePsInventory(inventoryData, config)
      } catch {
        return emptyInventory('ps-inventory')
      }
    },
    
    async getGloveboxInventory(plate: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT items FROM gloveboxitems WHERE plate = ?`,
          [plate]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('ps-inventory')
        }
        
        const inventoryData = parseJson(rows[0].items)
        return parsePsInventory(inventoryData, config)
      } catch {
        return emptyInventory('ps-inventory')
      }
    },
  }
}

function parsePsInventory(data: unknown, config: InventoryImageConfig): NormalizedInventory {
  // PS-inventory uses same format as QB-inventory
  const result = parseQbInventory(data, config)
  result.inventoryType = 'ps-inventory'
  return result
}

// ==========================================
// QS-INVENTORY ADAPTER
// ==========================================

async function createQsInventoryAdapter(): Promise<InventoryAdapter> {
  const config = await getInventoryImageConfig()
  
  return {
    async getCharacterInventory(characterId: string): Promise<NormalizedInventory> {
      try {
        // QS-inventory stores in inventory_items table
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT * FROM inventory_items WHERE owner = ?`,
          [characterId]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('qs-inventory')
        }
        
        return parseQsInventory(rows, config)
      } catch (error) {
        console.error('[QsInventory] Error:', error)
        return emptyInventory('qs-inventory')
      }
    },
    
    async getStashInventory(stashId: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT * FROM inventory_items WHERE inventory = ?`,
          [stashId]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('qs-inventory')
        }
        
        return parseQsInventory(rows, config)
      } catch {
        return emptyInventory('qs-inventory')
      }
    },
    
    async getTrunkInventory(plate: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT * FROM inventory_items WHERE inventory = ?`,
          [`trunk_${plate}`]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('qs-inventory')
        }
        
        return parseQsInventory(rows, config)
      } catch {
        return emptyInventory('qs-inventory')
      }
    },
    
    async getGloveboxInventory(plate: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT * FROM inventory_items WHERE inventory = ?`,
          [`glovebox_${plate}`]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('qs-inventory')
        }
        
        return parseQsInventory(rows, config)
      } catch {
        return emptyInventory('qs-inventory')
      }
    },
  }
}

function parseQsInventory(rows: RowDataPacket[], config: InventoryImageConfig): NormalizedInventory {
  const items: NormalizedItem[] = []
  let currentWeight = 0
  
  for (const row of rows) {
    if (row && row.item && row.amount > 0) {
      const metadata = parseJson(row.metadata) || {}
      const weight = row.weight || 0
      
      const normalizedItem: NormalizedItem = {
        name: row.item,
        label: row.label || row.item,
        count: row.amount || 1,
        weight,
        slot: row.slot || items.length + 1,
        description: row.description,
        image: row.item,
        imageUrl: buildItemImageUrl(row.item, config, 'qs-inventory'),
        metadata,
        unique: row.unique || false,
        useable: true,
        type: row.item?.toLowerCase().includes('weapon') ? 'weapon' : 'item',
      }
      items.push(normalizedItem)
      currentWeight += weight * (row.amount || 1)
    }
  }
  
  items.sort((a, b) => a.slot - b.slot)
  
  return {
    items,
    maxWeight: 120000,
    currentWeight,
    maxSlots: 50,
    usedSlots: items.length,
    inventoryType: 'qs-inventory',
  }
}

// ==========================================
// ESX INVENTORY ADAPTER
// ==========================================

async function createEsxInventoryAdapter(): Promise<InventoryAdapter> {
  const config = await getInventoryImageConfig()
  
  return {
    async getCharacterInventory(characterId: string): Promise<NormalizedInventory> {
      try {
        const rows = await queryGameDb<RowDataPacket[]>(
          `SELECT inventory, loadout FROM users WHERE identifier = ?`,
          [characterId]
        )
        
        if (!rows || rows.length === 0) {
          return emptyInventory('esx_inventory')
        }
        
        const inventoryData = parseJson(rows[0].inventory)
        const loadoutData = parseJson(rows[0].loadout)
        return parseEsxInventory(inventoryData, loadoutData, config)
      } catch (error) {
        console.error('[EsxInventory] Error:', error)
        return emptyInventory('esx_inventory')
      }
    },
    
    async getStashInventory(): Promise<NormalizedInventory> {
      return emptyInventory('esx_inventory')
    },
    
    async getTrunkInventory(): Promise<NormalizedInventory> {
      return emptyInventory('esx_inventory')
    },
    
    async getGloveboxInventory(): Promise<NormalizedInventory> {
      return emptyInventory('esx_inventory')
    },
  }
}

function parseEsxInventory(inventoryData: unknown, loadoutData: unknown, config: InventoryImageConfig): NormalizedInventory {
  const items: NormalizedItem[] = []
  let currentWeight = 0
  
  // Parse inventory items
  if (Array.isArray(inventoryData)) {
    for (const item of inventoryData) {
      if (item && item.count > 0) {
        const weight = item.weight || 0
        
        const normalizedItem: NormalizedItem = {
          name: item.name,
          label: item.label || item.name,
          count: item.count || 1,
          weight,
          slot: item.slot || items.length + 1,
          description: item.description,
          image: item.name,
          imageUrl: buildItemImageUrl(item.name, config, 'esx_inventory'),
          metadata: item.metadata || {},
          unique: item.unique || false,
          useable: true,
          type: 'item',
        }
        items.push(normalizedItem)
        currentWeight += weight * (item.count || 1)
      }
    }
  }
  
  // Parse weapons from loadout
  if (Array.isArray(loadoutData)) {
    for (const weapon of loadoutData) {
      if (weapon && weapon.name) {
        const normalizedItem: NormalizedItem = {
          name: weapon.name,
          label: weapon.label || weapon.name,
          count: 1,
          weight: 0,
          slot: items.length + 1,
          description: undefined,
          image: weapon.name,
          imageUrl: buildItemImageUrl(weapon.name, config, 'esx_inventory'),
          metadata: { ammo: weapon.ammo, components: weapon.components },
          unique: true,
          useable: true,
          type: 'weapon',
        }
        items.push(normalizedItem)
      }
    }
  }
  
  return {
    items,
    maxWeight: 100000,
    currentWeight,
    maxSlots: 40,
    usedSlots: items.length,
    inventoryType: 'esx_inventory',
  }
}

// ==========================================
// INVENTORY SERVICE
// ==========================================

export async function getInventoryAdapter(): Promise<InventoryAdapter> {
  const inventoryType = await detectInventorySystem()
  
  switch (inventoryType) {
    case 'ox_inventory':
      return createOxInventoryAdapter()
    case 'qb-inventory':
      return createQbInventoryAdapter()
    case 'ps-inventory':
      return createPsInventoryAdapter()
    case 'qs-inventory':
      return createQsInventoryAdapter()
    case 'esx_inventory':
      return createEsxInventoryAdapter()
    default:
      // Try to detect based on framework
      return createQbInventoryAdapter()
  }
}

export async function getCharacterInventory(characterId: string): Promise<NormalizedInventory> {
  const adapter = await getInventoryAdapter()
  return adapter.getCharacterInventory(characterId)
}

export async function getStashInventory(stashId: string): Promise<NormalizedInventory> {
  const adapter = await getInventoryAdapter()
  return adapter.getStashInventory(stashId)
}

export async function getTrunkInventory(plate: string): Promise<NormalizedInventory> {
  const adapter = await getInventoryAdapter()
  return adapter.getTrunkInventory(plate)
}

export async function getGloveboxInventory(plate: string): Promise<NormalizedInventory> {
  const adapter = await getInventoryAdapter()
  return adapter.getGloveboxInventory(plate)
}

// ==========================================
// HELPERS
// ==========================================

function emptyInventory(type: InventorySystemType): NormalizedInventory {
  return {
    items: [],
    maxWeight: 120000,
    currentWeight: 0,
    maxSlots: 40,
    usedSlots: 0,
    inventoryType: type,
  }
}

function parseJson(value: unknown): unknown {
  if (!value) return null
  if (typeof value === 'object') return value
  if (typeof value === 'string') {
    try {
      return JSON.parse(value)
    } catch {
      return null
    }
  }
  return null
}
