import { pikkuFunc } from '#pikku/pikku-types.gen.js'
import type { DB } from '@heygermany/sdk'
import {
  CandidateResults,
  DocumentStatuses,
  type QualificationCard,
  type GetCandidateResult,
  type SupportedLocale,
} from '@heygermany/sdk'
import jsonLogic from "json-logic-js"
import { createCandidateTextEngine } from "../../engines/candidate-text-engine.js"
import { createCandidateCardEngine } from "../../engines/candidate-card-engine.js"
import { TranslationService } from '../../services/translation-service.js'
import { isBackOffice, isCandidate } from '../../permissions.js'
import { NotFoundError } from '@pikku/core/errors'


// Helper function to determine document status
const getDocumentStatus = (documents: Pick<DB.CandidateDocument, 'category' | 'documentStatus'>[], category: string): DB.DocumentStatus => {
  const categoryDocs = documents.filter(d => d.category === category)
  if (categoryDocs.length === 0) {
    return DocumentStatuses.MISSING // No documents = Missing
  }
  // If any document is verified, consider it verified
  if (categoryDocs.some(d => d.documentStatus === DocumentStatuses.VERIFIED)) {
    return DocumentStatuses.VERIFIED
  }
  // If any document is invalid, consider it invalid
  if (categoryDocs.some(d => d.documentStatus === DocumentStatuses.INVALID)) {
    return DocumentStatuses.INVALID
  }
  // Documents exist but none are verified or invalid = unverified
  return DocumentStatuses.UNVERIFIED
}

type OutputBlock = {
  hero: string
  result: string
  explanation: string[]
  recognition?: string[]
}

function getByPath<T = any>(obj: any, path: string): T | undefined {
  return path.split(".").reduce((acc, k) => (acc ? acc[k] : undefined), obj)
}

function interpolate(str: string, vars: Record<string, string | number>) {
  return str.replace(/\{\{(\w+)\}\}/g, (_, k) => String(vars[k] ?? ""))
}

function withInterpolation(block: OutputBlock, vars: Record<string, string | number>): OutputBlock {
  return {
    hero: interpolate(block.hero, vars),
    result: interpolate(block.result, vars),
    explanation: block.explanation.map((s) => interpolate(s, vars)),
    recognition: block.recognition?.map((s) => interpolate(s, vars)),
  }
}

function applyNotes(
  translation: TranslationService,
  candidate: GetCandidateResult,
  locale: string = 'en'
): { afterExplanation: string[] } {
  const CANDIDATE_TEXT_ENGINE = createCandidateTextEngine(translation, locale)
  const notes = CANDIDATE_TEXT_ENGINE.notes
  const afterExplanation: string[] = []

  for (const n of notes) {
    if (jsonLogic.apply(n.when as any, candidate)) {
      if (n.attach?.where === "afterExplanation") {
        // Use textKey with locale lookup if available, fallback to text
        const noteText = (n as any).textKey
          ? translation.t((n as any).textKey, locale, (n as any).textKey)
          : (n as any).text || '[Missing note text]'
        afterExplanation.push(noteText)
      }
    }
  }
  return { afterExplanation }
}

function buildCandidateResults(translation: TranslationService, candidate: GetCandidateResult, locale: string): CandidateResults {
  const CANDIDATE_TEXT_ENGINE = createCandidateTextEngine(translation, locale)
  const CANDIDATE_CARD_ENGINE = createCandidateCardEngine(translation, locale)

  // Determine render path first (needed for next steps)
  let renderPath: string | null = null
  for (const r of CANDIDATE_TEXT_ENGINE.rules) {
    if (jsonLogic.apply(r.when as any, candidate)) {
      renderPath = r.render
      break
    }
  }
  // EDUCATION
  const eduRule = CANDIDATE_CARD_ENGINE.education.rules.find((r) =>
    jsonLogic.apply(r.when as any, candidate)
  )!
  const education: QualificationCard = {
    title: translation.t('education.title', locale),
    description:
      eduRule.status === "completed"
        ? CANDIDATE_CARD_ENGINE.education.getCompletedDescription()
        : CANDIDATE_CARD_ENGINE.education.getRequiredDescription(),
    status: eduRule.status as QualificationCard['status'],
    requirements: [...CANDIDATE_CARD_ENGINE.education.getRequirements()],
    steps:
      eduRule.status === "required"
        ? [CANDIDATE_CARD_ENGINE.education.pickNextStep(candidate)].filter(Boolean) as string[]
        : undefined,
  }

  // LANGUAGE
  const language = CANDIDATE_CARD_ENGINE.language.build(candidate)

  // LICENSE
  const licRule = CANDIDATE_CARD_ENGINE.license.rules.find((r) =>
    jsonLogic.apply(r.when as any, candidate)
  )!
  const license: QualificationCard = {
    title: translation.t('license.title', locale),
    description:
      licRule.status === "completed"
        ? CANDIDATE_CARD_ENGINE.license.getDescriptionCompleted()
        : CANDIDATE_CARD_ENGINE.license.getDescriptionRequired(),
    status: licRule.status as QualificationCard['status'],
    requirements: [...CANDIDATE_CARD_ENGINE.license.getRequirements()],
    steps: licRule.status === "required" ? [...CANDIDATE_CARD_ENGINE.license.getSteps()] : undefined,
  }

  // ADDITIONAL (3 subcards)
  const experience = CANDIDATE_CARD_ENGINE.additional.experience(candidate)
  const copies = CANDIDATE_CARD_ENGINE.additional.copies()
  const originals = CANDIDATE_CARD_ENGINE.additional.originals()

  // TEXT CONTENT
  // Fallback if nothing matched (be explicit)
  let textContent: CandidateResults['textContent']
  if (!renderPath) {
    console.log('No matching render path for candidate:', candidate)
    textContent = {
      renderPath: null,
      hero: "No eligible template for this candidate.",
      result: "",
      explanation: [],
      recognition: undefined,
      notes: { afterExplanation: [] as string[] },
    }
  } else {
    // 2) Fetch output block by render path (e.g., "nurse.missingLicense")
    const outputs = CANDIDATE_TEXT_ENGINE.getOutputs()
    const template = getByPath<OutputBlock>(outputs, renderPath.toLowerCase())
    if (!template) {
      textContent = {
        renderPath,
        hero: "Template not found.",
        result: "",
        explanation: [],
        recognition: undefined,
        notes: { afterExplanation: [] as string[] },
      }
    } else {
      // 3) Evaluate notes separately
      const notesApplied = applyNotes(translation, candidate, locale)

      // 4) Interpolate {{name}} (extend vars as needed)
      const vars = { name: candidate.name ?? "" }

      const interpolated = withInterpolation(template, vars)

      textContent = {
        renderPath,
        hero: interpolated.hero,
        result: interpolated.result,
        explanation: interpolated.explanation,
        recognition: interpolated.recognition,
        notes: notesApplied,
      }
    }
  }

  // Get next steps with pathway-specific overrides and conditional german step
  const nextSteps = CANDIDATE_TEXT_ENGINE.getNextSteps(renderPath, candidate)

  return {
    education,
    license,
    language,
    experience,
    copies,
    originals,
    joinTalentPool: candidate.joinTalentPool,
    nextSteps,
    textContent,
  }
}


export const getCandidateResult = pikkuFunc<
  Pick<DB.Candidate, 'candidateId'>,
  GetCandidateResult
>({
  expose: true,
  permissions: { isBackOffice, isCandidate },
  title: 'Get candidate qualification result',
  description: 'Returns qualification summary data for a candidate',
  errors: [NotFoundError],
  func: async ({ kysely, logger }, { candidateId }) => {
    const { durationRequirementsMet, ...candidate } = await kysely
      .selectFrom('candidate')
      .select([
        'durationRequirementsMet',
        'name',
        'joinTalentPool',
        'qualificationStatusGermany',
        'qualificationStatusHomeCountry',
        'euPassport',
        'livingInGermany',
        'licenseMandatory',
        'languageSelfAssessmentLevel',
        'languageCertificateProvided'
      ])
      .where('candidateId', '=', candidateId)
      .executeTakeFirstOrThrow(() => new NotFoundError('Candidate not found'))

    // Get candidate-level education analysis.
    const education = await kysely
      .selectFrom('candidateEducation')
      .select([
        'nursingRelatedDegree',
        'accreditedUniversity',
        'accreditedStudyProgram',
        'isNursingFocus',
        'hasTheory',
        'hasPractice'
      ])
      .where('candidateId', '=', candidateId)
      .executeTakeFirst()

    // Language analysis is now candidate-level (no document linkage).
    const languageDocs = await kysely
      .selectFrom('candidateLanguage')
      .select(['level'])
      .where('candidateLanguage.candidateId', '=', candidateId)
      .orderBy('level', 'desc')
      .executeTakeFirst()

    const hasPracticeHours = education?.hasPractice ?? null
    const hasTheoryHours = education?.hasTheory ?? null
    const isNursingFocus = education?.isNursingFocus ?? null

    // Get all documents to determine status
    const documents = await kysely
      .selectFrom('candidateDocument')
      .select(['category', 'documentStatus'])
      .where('candidateId', '=', candidateId)
      .execute()

    const licenseStatus = getDocumentStatus(documents, 'license')
    const cvStatus = getDocumentStatus(documents, 'work_experience')
    const languageStatus = getDocumentStatus(documents, 'language')

    const nursingRelatedDegree = education?.nursingRelatedDegree ?? null
    const accreditedUniversity = education?.accreditedUniversity ?? null
    const accreditedStudyProgram = education?.accreditedStudyProgram ?? null

    const structuredTrainingOverall =
      hasPracticeHours === null ||
      hasTheoryHours === null ||
      isNursingFocus === null ||
      durationRequirementsMet === null
        ? null
        : (hasPracticeHours && hasTheoryHours && isNursingFocus && durationRequirementsMet)
    const languageLevel = candidate.languageSelfAssessmentLevel || languageDocs?.level

    // Determine language assessment type
    const languageAssessmentType = languageDocs ? 'certificate' : 'self_assessment'

    // Check if all required fields are not null for validation
    const isValid = [
      candidate.qualificationStatusGermany,
      candidate.qualificationStatusHomeCountry,
      candidate.euPassport,
      candidate.livingInGermany,
      candidate.licenseMandatory,
      languageLevel,
      nursingRelatedDegree,
      accreditedUniversity,
      accreditedStudyProgram,
      structuredTrainingOverall
    ].every(value => value !== null && value !== undefined)

    if (!isValid) {
      logger.info(`Candidate ${candidateId} data is incomplete for validation`, {
        qualificationStatusGermany: candidate.qualificationStatusGermany,
        qualificationStatusHomeCountry: candidate.qualificationStatusHomeCountry,
        euPassport: candidate.euPassport,
        livingInGermany: candidate.livingInGermany,
        licenseMandatory: candidate.licenseMandatory,
        languageLevel,
        nursingRelatedDegree,
        accreditedUniversity,
        accreditedStudyProgram,
        structuredTrainingOverall
      })
    }

    return {
      // Fields from candidate table
      ...candidate,

      languageLevel,
      languageAssessmentType,

      // Aggregated education fields
      accreditedUniversity,
      accreditedStudyProgram,
      nursingRelatedDegree,

      // Transcript with sub-fields
      structuredTraining: {
        hasDuration: durationRequirementsMet,
        isNursingFocus,
        hasTheoryHours,
        hasPracticeHours,
        overallStatus: structuredTrainingOverall
      },

      // Document status
      docs: {
        cv: cvStatus,
        language: languageStatus,
        license: licenseStatus
      },

      // Validation
      isValid
    }
  }
})


export const getCandidateResults = pikkuFunc<
  { candidateId: string, locale: SupportedLocale },
  CandidateResults
>({
  title: 'Get candidate qualification results',
  description: 'Returns qualification cards for education, license, language, and additional requirements',
  permissions: {
    isBackOffice,
    isCandidate
  },
  func: async ({ translation }, { candidateId, locale = 'en' }, { rpc }) => {
    const candidate = await rpc.invoke('getCandidateResult', { candidateId })
    const results = buildCandidateResults(translation, candidate, locale)
    return results
  }
})
