import 'reflect-metadata'

import { Brackets, type Repository } from 'typeorm'

import {
  StudentProfileEntitySchema,
  type StudentProfileEntity
} from '../entities/StudentProfileEntity'
import { ClassEntitySchema, type ClassEntity } from '../entities/ClassEntity'
import type { ChildProfile, ClassProfile, ListStudentProfilesInput } from '../types/student'
import { getAppDataSource } from './databaseService'
import { getChatCompletionsUrl, logLargeModelRequest } from './modelService'
import { loadSettings } from './settingsService'

function parseJsonList(value: string): string[] {
  try {
    const parsed = JSON.parse(value)
    return Array.isArray(parsed) ? parsed : []
  } catch {
    return []
  }
}

function stringifyList(items: string[]): string {
  return JSON.stringify(items)
}

function normalizeClassType(type: string): 'cheap' | 'noble' {
  return type === 'noble' ? 'noble' : 'cheap'
}

function mapClassEntity(entity: ClassEntity): ClassProfile {
  return {
    id: entity.id,
    name: entity.name,
    type: normalizeClassType(entity.type),
    familyPhoto: entity.familyPhoto ?? undefined
  }
}

function mapChildEntity(entity: StudentProfileEntity): ChildProfile {
  return {
    id: entity.id,
    classId: entity.classId,
    className: entity.className,
    name: entity.name,
    englishName: entity.englishName,
    gender: entity.gender,
    birthday: entity.birthday,
    zodiac: entity.zodiac,
    friends: parseJsonList(entity.friends),
    hobbies: parseJsonList(entity.hobbies),
    favoriteGames: parseJsonList(entity.favoriteGames),
    favoriteFoods: parseJsonList(entity.favoriteFoods),
    traits: entity.traits,
    comment: entity.comment ?? '',
    commentGeneratedAt: entity.commentGeneratedAt ?? '',
    importedAt: entity.importedAt
  }
}

function mapClassProfile(profile: ClassProfile): ClassEntity {
  return {
    id: profile.id,
    name: profile.name,
    type: profile.type,
    familyPhoto: profile.familyPhoto ?? null
  }
}

function mapChildProfile(profile: ChildProfile, classItem?: ClassProfile): StudentProfileEntity {
  return {
    id: profile.id,
    classId: classItem?.id ?? profile.classId,
    className: classItem?.name ?? profile.className,
    name: profile.name,
    englishName: profile.englishName,
    gender: profile.gender,
    birthday: profile.birthday,
    zodiac: profile.zodiac,
    friends: stringifyList(profile.friends),
    hobbies: stringifyList(profile.hobbies),
    favoriteGames: stringifyList(profile.favoriteGames),
    favoriteFoods: stringifyList(profile.favoriteFoods),
    traits: profile.traits,
    comment: profile.comment ?? '',
    commentGeneratedAt: profile.commentGeneratedAt ?? '',
    importedAt: profile.importedAt
  }
}

function buildStudentCommentPrompt(profile: ChildProfile): string {
  return [
    '请为这名幼儿生成一段成长报告评语。',
    '要求：语气温暖、具体、积极，适合幼儿园成长报告；不要编造资料里没有的姓名、日期或家庭信息；长度控制在 120 到 180 字。',
    '',
    `姓名：${profile.name || '未填写'}`,
    `英文名：${profile.englishName || '未填写'}`,
    `班级：${profile.className || '未分班'}`,
    `性别：${profile.gender || '未填写'}`,
    `生日：${profile.birthday || '未填写'}`,
    `属相：${profile.zodiac || '未填写'}`,
    `好朋友：${profile.friends.length > 0 ? profile.friends.join('、') : '未填写'}`,
    `爱好：${profile.hobbies.length > 0 ? profile.hobbies.join('、') : '未填写'}`,
    `喜欢的游戏：${profile.favoriteGames.length > 0 ? profile.favoriteGames.join('、') : '未填写'}`,
    `喜欢吃的食物：${profile.favoriteFoods.length > 0 ? profile.favoriteFoods.join('、') : '未填写'}`,
    `表现特征：${profile.traits || '未填写'}`
  ].join('\n')
}

async function getRepositories(): Promise<{
  classRepository: Repository<ClassEntity>
  childRepository: Repository<StudentProfileEntity>
}> {
  const source = await getAppDataSource()

  return {
    classRepository: source.getRepository<ClassEntity>(ClassEntitySchema),
    childRepository: source.getRepository<StudentProfileEntity>(StudentProfileEntitySchema)
  }
}

export async function loadStudentData(): Promise<{
  classes: ClassProfile[]
  profiles: ChildProfile[]
}> {
  const { classRepository, childRepository } = await getRepositories()
  const classes = await classRepository.find({
    order: {
      id: 'ASC'
    }
  })
  const profiles = await childRepository.find({
    order: {
      id: 'ASC'
    }
  })

  return {
    classes: classes.map(mapClassEntity),
    profiles: profiles.map(mapChildEntity)
  }
}

export async function listStudentProfiles(input: ListStudentProfilesInput): Promise<{
  profiles: ChildProfile[]
  total: number
  page: number
  pageSize: number
}> {
  const pageSize = Math.min(Math.max(Number(input.pageSize) || 10, 1), 100)
  const page = Math.max(Number(input.page) || 1, 1)
  const keyword = input.query?.trim()
  const source = await getAppDataSource()
  const queryBuilder = source
    .getRepository<StudentProfileEntity>(StudentProfileEntitySchema)
    .createQueryBuilder('profile')
    .orderBy('profile.className', 'ASC')
    .addOrderBy('profile.name', 'ASC')
    .addOrderBy('profile.id', 'ASC')

  if (input.classId && input.classId !== 'all') {
    queryBuilder.andWhere('profile.classId = :classId', { classId: input.classId })
  }

  if (keyword) {
    queryBuilder.andWhere(
      new Brackets((builder) => {
        builder
          .where('profile.className LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.name LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.englishName LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.gender LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.birthday LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.zodiac LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.friends LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.hobbies LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.favoriteGames LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.favoriteFoods LIKE :keyword', { keyword: `%${keyword}%` })
          .orWhere('profile.traits LIKE :keyword', { keyword: `%${keyword}%` })
      })
    )
  }

  const [profiles, total] = await queryBuilder
    .skip((page - 1) * pageSize)
    .take(pageSize)
    .getManyAndCount()

  return {
    profiles: profiles.map(mapChildEntity),
    total,
    page,
    pageSize
  }
}

export async function updateStudentProfile(profile: ChildProfile): Promise<ChildProfile> {
  const source = await getAppDataSource()
  const repository = source.getRepository<StudentProfileEntity>(StudentProfileEntitySchema)
  const existingProfile = await repository.findOneBy({ id: profile.id })

  if (!existingProfile) {
    throw new Error('学生信息不存在')
  }

  const nextProfile: ChildProfile = {
    ...profile,
    comment: profile.comment ?? '',
    commentGeneratedAt: profile.commentGeneratedAt ?? existingProfile.commentGeneratedAt ?? ''
  }

  await repository.save(mapChildProfile(nextProfile))
  return nextProfile
}

export async function generateStudentComment(profileId: string): Promise<ChildProfile> {
  const source = await getAppDataSource()
  const repository = source.getRepository<StudentProfileEntity>(StudentProfileEntitySchema)
  const entity = await repository.findOneBy({ id: profileId })

  if (!entity) {
    throw new Error('学生信息不存在')
  }

  const profile = mapChildEntity(entity)
  const settings = await loadSettings()

  if (!settings.ok) {
    throw new Error(settings.message)
  }

  const modelConfig = settings.modelConfig

  if (!modelConfig?.baseUrl || !modelConfig.apiKey || !modelConfig.model) {
    throw new Error('请先在设置中配置模型接口、API Key 和模型')
  }

  const requestUrl = getChatCompletionsUrl(modelConfig.baseUrl)
  const userPrompt = buildStudentCommentPrompt(profile)

  logLargeModelRequest({
    label: '生成学生评语',
    url: requestUrl,
    apiKey: modelConfig.apiKey,
    model: modelConfig.model,
    prompt: userPrompt
  })

  const response = await fetch(requestUrl, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${modelConfig.apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      model: modelConfig.model,
      temperature: Number(modelConfig.temperature || 0.7),
      max_tokens: Number(modelConfig.maxTokens || 512),
      messages: [
        {
          role: 'system',
          content:
            modelConfig.systemPrompt ||
            '你是一名幼儿园老师，擅长撰写真诚、具体、积极的成长报告评语。'
        },
        {
          role: 'user',
          content: userPrompt
        }
      ]
    })
  })

  if (!response.ok) {
    throw new Error(`生成评语失败：HTTP ${response.status}`)
  }

  const result = (await response.json()) as {
    choices?: Array<{ message?: { content?: unknown } }>
  }
  const comment = result.choices?.[0]?.message?.content

  if (typeof comment !== 'string' || !comment.trim()) {
    throw new Error('模型没有返回可用评语')
  }

  const nextProfile: ChildProfile = {
    ...profile,
    comment: comment.trim(),
    commentGeneratedAt: new Date().toLocaleString('zh-CN', { hour12: false })
  }

  await repository.save(mapChildProfile(nextProfile))
  return nextProfile
}

export async function saveClasses(classes: ClassProfile[]): Promise<void> {
  const source = await getAppDataSource()

  await source.transaction(async (manager) => {
    const classRepository = manager.getRepository<ClassEntity>(ClassEntitySchema)
    const childRepository = manager.getRepository<StudentProfileEntity>(StudentProfileEntitySchema)
    const nextClassIds = new Set(classes.map((classItem) => classItem.id))
    const existingClasses = await classRepository.find()

    for (const classItem of existingClasses) {
      if (!nextClassIds.has(classItem.id)) {
        await childRepository.delete({ classId: classItem.id })
        await classRepository.delete({ id: classItem.id })
      }
    }

    for (const classItem of classes) {
      await classRepository.save(mapClassProfile(classItem))
      await childRepository.update({ classId: classItem.id }, { className: classItem.name })
    }
  })
}

export async function migrateLocalStudentData(
  classes: ClassProfile[],
  profiles: ChildProfile[]
): Promise<void> {
  const source = await getAppDataSource()

  await source.transaction(async (manager) => {
    const classRepository = manager.getRepository<ClassEntity>(ClassEntitySchema)
    const childRepository = manager.getRepository<StudentProfileEntity>(StudentProfileEntitySchema)
    const classCount = await classRepository.count()
    const profileCount = await childRepository.count()

    if (classCount > 0 || profileCount > 0) {
      return
    }

    await classRepository.save(classes.map(mapClassProfile))
    await childRepository.save(profiles.map((profile) => mapChildProfile(profile)))
  })
}

export async function replaceProfilesForClass(
  classItem: ClassProfile,
  profiles: ChildProfile[]
): Promise<void> {
  const source = await getAppDataSource()

  await source.transaction(async (manager) => {
    const childRepository = manager.getRepository<StudentProfileEntity>(StudentProfileEntitySchema)

    await childRepository.delete({ classId: classItem.id })
    await childRepository.save(profiles.map((profile) => mapChildProfile(profile, classItem)))
  })
}

export async function deleteClassWithProfiles(classId: string): Promise<void> {
  const source = await getAppDataSource()

  await source.transaction(async (manager) => {
    await manager.getRepository<StudentProfileEntity>(StudentProfileEntitySchema).delete({ classId })
    await manager.getRepository<ClassEntity>(ClassEntitySchema).delete({ id: classId })
  })
}

export async function deleteStudentProfile(profileId: string): Promise<void> {
  const source = await getAppDataSource()
  const result = await source
    .getRepository<StudentProfileEntity>(StudentProfileEntitySchema)
    .delete({ id: profileId })

  if (!result.affected) {
    throw new Error('学生信息不存在')
  }
}
