import { supabase } from '@/lib/supabase'
import type { UserGamification, XuTransaction, UserLevel } from '@/types'

// XP awarded per action type
export const XP_REWARDS = { test: 20, writing: 15 } as const

// Xu awarded at streak milestones
const STREAK_XU: Record<number, number> = { 7: 20, 30: 50, 100: 100 }

function calcLevel(xp: number): UserLevel {
  if (xp >= 10000) return 'master'
  if (xp >= 5000) return 'gold'
  if (xp >= 2000) return 'silver'
  if (xp >= 500) return 'bronze'
  return 'beginner'
}

function today(): string {
  return new Date().toISOString().split('T')[0]
}

function yesterday(): string {
  const d = new Date()
  d.setDate(d.getDate() - 1)
  return d.toISOString().split('T')[0]
}

export function getWeekStart(): string {
  const d = new Date()
  const day = d.getDay()
  const diff = day === 0 ? -6 : 1 - day
  d.setDate(d.getDate() + diff)
  return d.toISOString().split('T')[0]
}

// Map Supabase snake_case row → camelCase type
function mapGamification(row: Record<string, unknown>): UserGamification {
  return {
    userId: row.user_id as string,
    xp: row.xp as number,
    level: row.level as UserLevel,
    streak: row.streak as number,
    longestStreak: row.longest_streak as number,
    lastActive: (row.last_active as string) ?? null,
    xu: row.xu as number,
    freezeCount: row.freeze_count as number,
    createdAt: row.created_at as string,
  }
}

// ─── Fetch ────────────────────────────────────────────────────────────────────

export async function fetchGamification(userId: string): Promise<UserGamification | null> {
  const { data, error } = await supabase
    .from('user_gamification')
    .select('*')
    .eq('user_id', userId)
    .single()
  if (error) {
    if (error.code === 'PGRST116') return null
    throw error
  }
  return mapGamification(data as Record<string, unknown>)
}

export async function fetchXuTransactions(userId: string, limit = 10): Promise<XuTransaction[]> {
  const { data, error } = await supabase
    .from('xu_transactions')
    .select('*')
    .eq('user_id', userId)
    .order('created_at', { ascending: false })
    .limit(limit)
  if (error) throw error
  return (data ?? []).map((row: Record<string, unknown>) => ({
    id: row.id as string,
    userId: row.user_id as string,
    type: row.type as XuTransaction['type'],
    amount: row.amount as number,
    balance: row.balance as number,
    description: (row.description as string) ?? null,
    createdAt: row.created_at as string,
  }))
}

export interface LeaderboardRow {
  userId: string
  displayName: string
  xpEarned: number
  rank: number
}

export async function fetchLeaderboard(): Promise<LeaderboardRow[]> {
  const weekStart = getWeekStart()

  // Step 1: fetch leaderboard rankings
  const { data: lbRows, error: lbErr } = await supabase
    .from('weekly_leaderboard')
    .select('user_id, xp_earned')
    .eq('week_start', weekStart)
    .order('xp_earned', { ascending: false })
    .limit(20)
  if (lbErr) throw lbErr
  if (!lbRows?.length) return []

  // Step 2: fetch display names from user_gamification
  const userIds = lbRows.map((r) => r.user_id)
  const { data: gamRows } = await supabase
    .from('user_gamification')
    .select('user_id, display_name')
    .in('user_id', userIds)

  const nameMap = new Map((gamRows ?? []).map((r) => [r.user_id, r.display_name ?? 'Người dùng']))

  return lbRows.map((row, i) => ({
    userId: row.user_id,
    displayName: nameMap.get(row.user_id) ?? 'Người dùng',
    xpEarned: row.xp_earned,
    rank: i + 1,
  }))
}

// ─── Award Activity ───────────────────────────────────────────────────────────

export interface AwardResult {
  xpGained: number
  xuGained: number
  newStreak: number
  levelUp: boolean
}

export async function awardActivity(
  userId: string,
  xpAmount: number,
  displayName: string,
): Promise<AwardResult> {
  const todayStr = today()
  const current = await fetchGamification(userId)

  const prevXp = current?.xp ?? 0
  const prevXu = current?.xu ?? 50
  const prevStreak = current?.streak ?? 0
  const lastActive = current?.lastActive ?? null

  // Streak logic
  const isNewDay = lastActive !== todayStr
  const isConsecutive = lastActive === yesterday()
  let newStreak: number
  if (!lastActive) newStreak = 1
  else if (!isNewDay) newStreak = prevStreak
  else if (isConsecutive) newStreak = prevStreak + 1
  else newStreak = 1 // streak broken

  // XP + level
  const newXp = prevXp + xpAmount
  const newLevel = calcLevel(newXp)
  const levelUp = newLevel !== calcLevel(prevXp)

  // Xu (daily goal + streak milestones, once per day)
  let xuGained = 0
  const txRows: Array<{ user_id: string; type: string; amount: number; balance: number; description: string }> = []

  if (isNewDay) {
    xuGained += 10
    txRows.push({ user_id: userId, type: 'earn_daily', amount: 10, balance: 0, description: 'Hoàn thành mục tiêu ngày' })
  }
  if (isNewDay && STREAK_XU[newStreak]) {
    const bonus = STREAK_XU[newStreak]
    xuGained += bonus
    txRows.push({ user_id: userId, type: 'earn_streak', amount: bonus, balance: 0, description: `Streak ${newStreak} ngày` })
  }

  const newXu = prevXu + xuGained
  // Back-fill running balance in transaction rows
  txRows.forEach((t, i) => { t.balance = prevXu + txRows.slice(0, i + 1).reduce((s, r) => s + r.amount, 0) })

  // Upsert gamification state
  await supabase.from('user_gamification').upsert({
    user_id: userId,
    xp: newXp,
    level: newLevel,
    streak: newStreak,
    longest_streak: Math.max(current?.longestStreak ?? 0, newStreak),
    last_active: todayStr,
    xu: newXu,
    freeze_count: current?.freezeCount ?? 0,
    display_name: displayName,
  }, { onConflict: 'user_id' })

  if (txRows.length > 0) {
    await supabase.from('xu_transactions').insert(txRows)
  }

  // Accumulate XP on weekly leaderboard
  const weekStart = getWeekStart()
  const { data: existing } = await supabase
    .from('weekly_leaderboard')
    .select('xp_earned')
    .eq('user_id', userId)
    .eq('week_start', weekStart)
    .single()

  await supabase.from('weekly_leaderboard').upsert({
    user_id: userId,
    week_start: weekStart,
    xp_earned: (existing?.xp_earned ?? 0) + xpAmount,
  }, { onConflict: 'user_id,week_start' })

  return { xpGained: xpAmount, xuGained, newStreak, levelUp }
}
