import { PrismaClient, OceanIssue, CompetitionCategory } from '@prisma/client'
import { getOpenAI, getConfiguredModel } from '@/lib/openai'

interface ProjectInfo {
  id: string
  title: string
  description: string | null
  oceanIssue: OceanIssue | null
  competitionCategory: CompetitionCategory | null
  tags: string[]
}

interface MentorInfo {
  id: string
  name: string | null
  email: string
  expertiseTags: string[]
  currentAssignments: number
  maxAssignments: number | null
}

interface MentorMatch {
  mentorId: string
  confidenceScore: number
  expertiseMatchScore: number
  reasoning: string
}

/**
 * Get AI-suggested mentor matches for a project
 */
export async function getAIMentorSuggestions(
  prisma: PrismaClient,
  projectId: string,
  limit: number = 5
): Promise<MentorMatch[]> {
  // Get project details
  const project = await prisma.project.findUniqueOrThrow({
    where: { id: projectId },
    select: {
      id: true,
      title: true,
      description: true,
      oceanIssue: true,
      competitionCategory: true,
      tags: true,
    },
  })

  // Get available mentors (users with expertise tags)
  // In a full implementation, you'd have a MENTOR role
  // For now, we use users with expertiseTags and consider them potential mentors
  const mentors = await prisma.user.findMany({
    where: {
      OR: [
        { expertiseTags: { isEmpty: false } },
        { role: 'JURY_MEMBER' }, // Jury members can also be mentors
      ],
      status: 'ACTIVE',
    },
    select: {
      id: true,
      name: true,
      email: true,
      expertiseTags: true,
      maxAssignments: true,
      mentorAssignments: {
        select: { id: true },
      },
    },
  })

  // Filter mentors who haven't reached max assignments
  const availableMentors: MentorInfo[] = mentors
    .filter((m) => {
      const currentAssignments = m.mentorAssignments.length
      return !m.maxAssignments || currentAssignments < m.maxAssignments
    })
    .map((m) => ({
      id: m.id,
      name: m.name,
      email: m.email,
      expertiseTags: m.expertiseTags,
      currentAssignments: m.mentorAssignments.length,
      maxAssignments: m.maxAssignments,
    }))

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

  // Try AI matching if API key is configured
  if (process.env.OPENAI_API_KEY) {
    try {
      return await getAIMatches(project, availableMentors, limit)
    } catch (error) {
      console.error('AI mentor matching failed, falling back to algorithm:', error)
    }
  }

  // Fallback to algorithmic matching
  return getAlgorithmicMatches(project, availableMentors, limit)
}

/**
 * Use OpenAI to match mentors to projects
 */
async function getAIMatches(
  project: ProjectInfo,
  mentors: MentorInfo[],
  limit: number
): Promise<MentorMatch[]> {
  // Anonymize data before sending to AI
  const anonymizedProject = {
    description: project.description?.slice(0, 500) || 'No description',
    category: project.competitionCategory,
    oceanIssue: project.oceanIssue,
    tags: project.tags,
  }

  const anonymizedMentors = mentors.map((m, index) => ({
    index,
    expertise: m.expertiseTags,
    availability: m.maxAssignments
      ? `${m.currentAssignments}/${m.maxAssignments}`
      : 'unlimited',
  }))

  const prompt = `You are matching mentors to an ocean protection project.

PROJECT:
- Category: ${anonymizedProject.category || 'Not specified'}
- Ocean Issue: ${anonymizedProject.oceanIssue || 'Not specified'}
- Tags: ${anonymizedProject.tags.join(', ') || 'None'}
- Description: ${anonymizedProject.description}

AVAILABLE MENTORS:
${anonymizedMentors.map((m) => `${m.index}: Expertise: [${m.expertise.join(', ')}], Availability: ${m.availability}`).join('\n')}

Rank the top ${limit} mentors by suitability. For each, provide:
1. Mentor index (0-based)
2. Confidence score (0-1)
3. Expertise match score (0-1)
4. Brief reasoning (1-2 sentences)

Respond in JSON format:
{
  "matches": [
    {
      "mentorIndex": 0,
      "confidenceScore": 0.85,
      "expertiseMatchScore": 0.9,
      "reasoning": "Strong expertise alignment..."
    }
  ]
}`

  const openai = await getOpenAI()
  if (!openai) {
    throw new Error('OpenAI client not available')
  }

  const model = await getConfiguredModel()

  const response = await openai.chat.completions.create({
    model,
    messages: [
      {
        role: 'system',
        content: 'You are an expert at matching mentors to projects based on expertise alignment. Always respond with valid JSON.',
      },
      { role: 'user', content: prompt },
    ],
    response_format: { type: 'json_object' },
    temperature: 0.3,
    max_tokens: 1000,
  })

  const content = response.choices[0]?.message?.content
  if (!content) {
    throw new Error('No response from AI')
  }

  const parsed = JSON.parse(content) as {
    matches: Array<{
      mentorIndex: number
      confidenceScore: number
      expertiseMatchScore: number
      reasoning: string
    }>
  }

  return parsed.matches
    .filter((m) => m.mentorIndex >= 0 && m.mentorIndex < mentors.length)
    .map((m) => ({
      mentorId: mentors[m.mentorIndex].id,
      confidenceScore: m.confidenceScore,
      expertiseMatchScore: m.expertiseMatchScore,
      reasoning: m.reasoning,
    }))
}

/**
 * Algorithmic fallback for mentor matching
 */
function getAlgorithmicMatches(
  project: ProjectInfo,
  mentors: MentorInfo[],
  limit: number
): MentorMatch[] {
  // Build keyword set from project
  const projectKeywords = new Set<string>()

  if (project.oceanIssue) {
    projectKeywords.add(project.oceanIssue.toLowerCase().replace(/_/g, ' '))
  }

  if (project.competitionCategory) {
    projectKeywords.add(project.competitionCategory.toLowerCase().replace(/_/g, ' '))
  }

  project.tags.forEach((tag) => {
    tag.toLowerCase().split(/\s+/).forEach((word) => {
      if (word.length > 3) projectKeywords.add(word)
    })
  })

  if (project.description) {
    // Extract key words from description
    const words = project.description.toLowerCase().split(/\s+/)
    words.forEach((word) => {
      if (word.length > 4) projectKeywords.add(word.replace(/[^a-z]/g, ''))
    })
  }

  // Score each mentor
  const scored = mentors.map((mentor) => {
    const mentorKeywords = new Set<string>()
    mentor.expertiseTags.forEach((tag) => {
      tag.toLowerCase().split(/\s+/).forEach((word) => {
        if (word.length > 2) mentorKeywords.add(word)
      })
    })

    // Calculate overlap
    let matchCount = 0
    projectKeywords.forEach((keyword) => {
      mentorKeywords.forEach((mentorKeyword) => {
        if (keyword.includes(mentorKeyword) || mentorKeyword.includes(keyword)) {
          matchCount++
        }
      })
    })

    const expertiseMatchScore = mentorKeywords.size > 0
      ? Math.min(1, matchCount / mentorKeywords.size)
      : 0

    // Factor in availability
    const availabilityScore = mentor.maxAssignments
      ? 1 - (mentor.currentAssignments / mentor.maxAssignments)
      : 1

    const confidenceScore = (expertiseMatchScore * 0.7 + availabilityScore * 0.3)

    return {
      mentorId: mentor.id,
      confidenceScore: Math.round(confidenceScore * 100) / 100,
      expertiseMatchScore: Math.round(expertiseMatchScore * 100) / 100,
      reasoning: `Matched ${matchCount} keyword(s) with mentor expertise. Availability: ${availabilityScore > 0.5 ? 'Good' : 'Limited'}.`,
    }
  })

  // Sort by confidence and return top matches
  return scored
    .sort((a, b) => b.confidenceScore - a.confidenceScore)
    .slice(0, limit)
}

/**
 * Round-robin assignment for load balancing
 */
export async function getRoundRobinMentor(
  prisma: PrismaClient,
  excludeMentorIds: string[] = []
): Promise<string | null> {
  const mentors = await prisma.user.findMany({
    where: {
      OR: [
        { expertiseTags: { isEmpty: false } },
        { role: 'JURY_MEMBER' },
      ],
      status: 'ACTIVE',
      id: { notIn: excludeMentorIds },
    },
    select: {
      id: true,
      maxAssignments: true,
      mentorAssignments: {
        select: { id: true },
      },
    },
    orderBy: {
      mentorAssignments: {
        _count: 'asc',
      },
    },
  })

  // Find mentor with fewest assignments who hasn't reached max
  for (const mentor of mentors) {
    const currentCount = mentor.mentorAssignments.length
    if (!mentor.maxAssignments || currentCount < mentor.maxAssignments) {
      return mentor.id
    }
  }

  return null
}
