/**
 * Smart Assignment Scoring Service
 *
 * Calculates scores for jury/mentor-project matching based on:
 * - Tag overlap (expertise match)
 * - Workload balance
 * - Country match (mentors only)
 *
 * Score Breakdown (100 points max):
 * - Tag overlap: 0-50 points (weighted by confidence)
 * - Workload balance: 0-25 points
 * - Country match: 0-15 points (mentors only)
 * - Reserved: 0-10 points (future AI boost)
 */

import { prisma } from '@/lib/prisma'

// ─── Types ──────────────────────────────────────────────────────────────────

export interface ScoreBreakdown {
  tagOverlap: number
  workloadBalance: number
  countryMatch: number
  aiBoost: number
}

export interface AssignmentScore {
  userId: string
  userName: string
  userEmail: string
  projectId: string
  projectTitle: string
  score: number
  breakdown: ScoreBreakdown
  reasoning: string[]
  matchingTags: string[]
}

export interface ProjectTagData {
  tagId: string
  tagName: string
  confidence: number
}

// ─── Constants ───────────────────────────────────────────────────────────────

const MAX_TAG_OVERLAP_SCORE = 50
const MAX_WORKLOAD_SCORE = 25
const MAX_COUNTRY_SCORE = 15
const POINTS_PER_TAG_MATCH = 10

// ─── Scoring Functions ───────────────────────────────────────────────────────

/**
 * Calculate tag overlap score between user expertise and project tags
 */
export function calculateTagOverlapScore(
  userTagNames: string[],
  projectTags: ProjectTagData[]
): { score: number; matchingTags: string[] } {
  if (projectTags.length === 0 || userTagNames.length === 0) {
    return { score: 0, matchingTags: [] }
  }

  const userTagSet = new Set(userTagNames.map((t) => t.toLowerCase()))
  const matchingTags: string[] = []
  let weightedScore = 0

  for (const pt of projectTags) {
    if (userTagSet.has(pt.tagName.toLowerCase())) {
      matchingTags.push(pt.tagName)
      // Weight by confidence - higher confidence = more points
      weightedScore += POINTS_PER_TAG_MATCH * pt.confidence
    }
  }

  // Cap at max score
  const score = Math.min(MAX_TAG_OVERLAP_SCORE, Math.round(weightedScore))
  return { score, matchingTags }
}

/**
 * Calculate workload balance score
 * Full points if under target, decreasing as over target
 */
export function calculateWorkloadScore(
  currentAssignments: number,
  targetAssignments: number,
  maxAssignments?: number | null
): number {
  // If user is at or over their personal max, return 0
  if (maxAssignments !== null && maxAssignments !== undefined) {
    if (currentAssignments >= maxAssignments) {
      return 0
    }
  }

  // If under target, full points
  if (currentAssignments < targetAssignments) {
    return MAX_WORKLOAD_SCORE
  }

  // Over target - decrease score
  const overload = currentAssignments - targetAssignments
  return Math.max(0, MAX_WORKLOAD_SCORE - overload * 5)
}

/**
 * Calculate country match score (mentors only)
 * Same country = bonus points
 */
export function calculateCountryMatchScore(
  userCountry: string | null | undefined,
  projectCountry: string | null | undefined
): number {
  if (!userCountry || !projectCountry) {
    return 0
  }

  // Normalize for comparison
  const normalizedUser = userCountry.toLowerCase().trim()
  const normalizedProject = projectCountry.toLowerCase().trim()

  if (normalizedUser === normalizedProject) {
    return MAX_COUNTRY_SCORE
  }

  return 0
}

// ─── Main Scoring Function ───────────────────────────────────────────────────

/**
 * Get smart assignment suggestions for a round
 */
export async function getSmartSuggestions(options: {
  roundId: string
  type: 'jury' | 'mentor'
  limit?: number
  aiMaxPerJudge?: number
}): Promise<AssignmentScore[]> {
  const { roundId, type, limit = 50, aiMaxPerJudge = 20 } = options

  // Get projects in round with their tags
  const projects = await prisma.project.findMany({
    where: {
      roundId,
      status: { not: 'REJECTED' },
    },
    include: {
      projectTags: {
        include: { tag: true },
      },
    },
  })

  if (projects.length === 0) {
    return []
  }

  // Get users of the appropriate role
  const role = type === 'jury' ? 'JURY_MEMBER' : 'MENTOR'
  const users = await prisma.user.findMany({
    where: {
      role,
      status: 'ACTIVE',
    },
    include: {
      _count: {
        select: {
          assignments: {
            where: { roundId },
          },
        },
      },
    },
  })

  if (users.length === 0) {
    return []
  }

  // Get existing assignments to avoid duplicates
  const existingAssignments = await prisma.assignment.findMany({
    where: { roundId },
    select: { userId: true, projectId: true },
  })
  const assignedPairs = new Set(
    existingAssignments.map((a) => `${a.userId}:${a.projectId}`)
  )

  // Calculate target assignments per user
  const targetPerUser = Math.ceil(projects.length / users.length)

  // Calculate scores for all user-project pairs
  const suggestions: AssignmentScore[] = []

  for (const user of users) {
    // Skip users at AI max (they won't appear in suggestions)
    const currentCount = user._count.assignments
    if (currentCount >= aiMaxPerJudge) {
      continue
    }

    for (const project of projects) {
      // Skip if already assigned
      const pairKey = `${user.id}:${project.id}`
      if (assignedPairs.has(pairKey)) {
        continue
      }

      // Get project tags data
      const projectTags: ProjectTagData[] = project.projectTags.map((pt) => ({
        tagId: pt.tagId,
        tagName: pt.tag.name,
        confidence: pt.confidence,
      }))

      // Calculate scores
      const { score: tagScore, matchingTags } = calculateTagOverlapScore(
        user.expertiseTags,
        projectTags
      )

      const workloadScore = calculateWorkloadScore(
        currentCount,
        targetPerUser,
        user.maxAssignments
      )

      // Country match only for mentors
      const countryScore =
        type === 'mentor'
          ? calculateCountryMatchScore(
              (user as any).country, // User might have country field
              project.country
            )
          : 0

      const totalScore = tagScore + workloadScore + countryScore

      // Build reasoning
      const reasoning: string[] = []
      if (matchingTags.length > 0) {
        reasoning.push(`Expertise match: ${matchingTags.length} tag(s)`)
      }
      if (workloadScore === MAX_WORKLOAD_SCORE) {
        reasoning.push('Available capacity')
      } else if (workloadScore > 0) {
        reasoning.push('Moderate workload')
      }
      if (countryScore > 0) {
        reasoning.push('Same country')
      }

      suggestions.push({
        userId: user.id,
        userName: user.name || 'Unknown',
        userEmail: user.email,
        projectId: project.id,
        projectTitle: project.title,
        score: totalScore,
        breakdown: {
          tagOverlap: tagScore,
          workloadBalance: workloadScore,
          countryMatch: countryScore,
          aiBoost: 0,
        },
        reasoning,
        matchingTags,
      })
    }
  }

  // Sort by score descending and limit
  return suggestions.sort((a, b) => b.score - a.score).slice(0, limit)
}

/**
 * Get mentor suggestions for a specific project
 */
export async function getMentorSuggestionsForProject(
  projectId: string,
  limit: number = 10
): Promise<AssignmentScore[]> {
  const project = await prisma.project.findUnique({
    where: { id: projectId },
    include: {
      projectTags: {
        include: { tag: true },
      },
      mentorAssignment: true,
    },
  })

  if (!project) {
    throw new Error(`Project not found: ${projectId}`)
  }

  // Get all active mentors
  const mentors = await prisma.user.findMany({
    where: {
      role: 'MENTOR',
      status: 'ACTIVE',
    },
    include: {
      _count: {
        select: { mentorAssignments: true },
      },
    },
  })

  if (mentors.length === 0) {
    return []
  }

  const projectTags: ProjectTagData[] = project.projectTags.map((pt) => ({
    tagId: pt.tagId,
    tagName: pt.tag.name,
    confidence: pt.confidence,
  }))

  const targetPerMentor = 5 // Target 5 projects per mentor

  const suggestions: AssignmentScore[] = []

  for (const mentor of mentors) {
    // Skip if already assigned to this project
    if (project.mentorAssignment?.mentorId === mentor.id) {
      continue
    }

    const { score: tagScore, matchingTags } = calculateTagOverlapScore(
      mentor.expertiseTags,
      projectTags
    )

    const workloadScore = calculateWorkloadScore(
      mentor._count.mentorAssignments,
      targetPerMentor,
      mentor.maxAssignments
    )

    const countryScore = calculateCountryMatchScore(
      (mentor as any).country,
      project.country
    )

    const totalScore = tagScore + workloadScore + countryScore

    const reasoning: string[] = []
    if (matchingTags.length > 0) {
      reasoning.push(`${matchingTags.length} matching expertise tag(s)`)
    }
    if (countryScore > 0) {
      reasoning.push('Same country of origin')
    }
    if (workloadScore === MAX_WORKLOAD_SCORE) {
      reasoning.push('Available capacity')
    }

    suggestions.push({
      userId: mentor.id,
      userName: mentor.name || 'Unknown',
      userEmail: mentor.email,
      projectId: project.id,
      projectTitle: project.title,
      score: totalScore,
      breakdown: {
        tagOverlap: tagScore,
        workloadBalance: workloadScore,
        countryMatch: countryScore,
        aiBoost: 0,
      },
      reasoning,
      matchingTags,
    })
  }

  return suggestions.sort((a, b) => b.score - a.score).slice(0, limit)
}
