import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { TranslationsService } from '../translations/translations.service';
import {
  AbilityType,
  Proficiency,
  FeatSource,
  TranslationType,
  CharacterType,
  ResearchField,
} from '../../generated/prisma/client.js';
import { PathbuilderJson, PathbuilderBuild } from './dto/pathbuilder-import.dto';

// Research field detection mapping
const RESEARCH_FIELD_FEATS: Record<string, ResearchField> = {
  'bomber': ResearchField.BOMBER,
  'chirurgeon': ResearchField.CHIRURGEON,
  'mutagenist': ResearchField.MUTAGENIST,
  'toxicologist': ResearchField.TOXICOLOGIST,
};

// Skill name mappings (English -> German)
const SKILL_TRANSLATIONS: Record<string, string> = {
  acrobatics: 'Akrobatik',
  arcana: 'Arkane Künste',
  athletics: 'Athletik',
  crafting: 'Handwerk',
  deception: 'Täuschung',
  diplomacy: 'Diplomatie',
  intimidation: 'Einschüchtern',
  medicine: 'Medizin',
  nature: 'Naturkunde',
  occultism: 'Okkultismus',
  performance: 'Darbietung',
  religion: 'Religionskunde',
  society: 'Gesellschaftskunde',
  stealth: 'Heimlichkeit',
  survival: 'Überleben',
  thievery: 'Diebeskunst',
};

// Proficiency value mappings (Pathbuilder uses 0, 2, 4, 6, 8)
function proficiencyFromValue(value: number): Proficiency {
  switch (value) {
    case 8: return Proficiency.LEGENDARY;
    case 6: return Proficiency.MASTER;
    case 4: return Proficiency.EXPERT;
    case 2: return Proficiency.TRAINED;
    default: return Proficiency.UNTRAINED;
  }
}

// Feat source mapping
function featSourceFromType(type: string): FeatSource {
  const normalized = type.toLowerCase();
  if (normalized.includes('class')) return FeatSource.CLASS;
  if (normalized.includes('ancestry') || normalized.includes('human')) return FeatSource.ANCESTRY;
  if (normalized.includes('skill')) return FeatSource.SKILL;
  if (normalized.includes('general')) return FeatSource.GENERAL;
  if (normalized.includes('archetype')) return FeatSource.ARCHETYPE;
  return FeatSource.BONUS;
}

@Injectable()
export class PathbuilderImportService {
  private readonly logger = new Logger(PathbuilderImportService.name);

  constructor(
    private prisma: PrismaService,
    private translationsService: TranslationsService,
  ) {}

  /**
   * Import a character from Pathbuilder JSON
   */
  async importCharacter(
    campaignId: string,
    ownerId: string,
    pathbuilderJson: PathbuilderJson,
  ) {
    const build = pathbuilderJson.build;
    this.logger.log(`Importing character: ${build.name} (Level ${build.level} ${build.class})`);

    // Calculate HP
    const conModifier = Math.floor((build.abilities.con - 10) / 2);
    const hpMax = build.attributes.ancestryhp +
      build.attributes.classhp +
      build.attributes.bonushp +
      (conModifier * build.level) +
      (build.attributes.bonushpPerLevel * build.level);

    // Translate basics
    const [classTranslation, ancestryTranslation, heritageTranslation, backgroundTranslation] =
      await Promise.all([
        this.translationsService.getTranslation(TranslationType.CLASS, build.class),
        this.translationsService.getTranslation(TranslationType.ANCESTRY, build.ancestry),
        this.translationsService.getTranslation(TranslationType.HERITAGE, build.heritage),
        this.translationsService.getTranslation(TranslationType.BACKGROUND, build.background),
      ]);

    // Convert money to credits (1 pp = 1000 cr, 1 gp = 100 cr, 1 sp = 10 cr, 1 cp = 1 cr)
    let credits = 0;
    if (build.money) {
      credits =
        (build.money.pp || 0) * 1000 +
        (build.money.gp || 0) * 100 +
        (build.money.sp || 0) * 10 +
        (build.money.cp || 0);
    }

    // Create character
    const character = await this.prisma.character.create({
      data: {
        campaignId,
        ownerId,
        name: build.name,
        type: CharacterType.PC,
        level: build.level,
        hpCurrent: hpMax,
        hpMax,
        hpTemp: 0,
        ancestryId: build.ancestry,
        heritageId: build.heritage,
        classId: build.class,
        backgroundId: build.background,
        experiencePoints: build.xp || 0,
        credits,
        pathbuilderData: JSON.parse(JSON.stringify({
          ...pathbuilderJson,
          translations: {
            class: classTranslation,
            ancestry: ancestryTranslation,
            heritage: heritageTranslation,
            background: backgroundTranslation,
          },
        })),
      },
    });

    this.logger.log(`Character created with ID: ${character.id}`);

    // Import all related data in parallel
    await Promise.all([
      this.importAbilities(character.id, build),
      this.importSkills(character.id, build),
      this.importFeats(character.id, build),
      this.importItems(character.id, build),
      this.importResources(character.id, build),
    ]);

    // Import alchemy data if character is an alchemist
    if (this.isAlchemist(build)) {
      await this.importAlchemy(character.id, build);
    }

    // Fetch complete character with relations
    return this.prisma.character.findUnique({
      where: { id: character.id },
      include: {
        owner: { select: { id: true, username: true } },
        abilities: true,
        skills: { orderBy: { skillName: 'asc' } },
        feats: { orderBy: [{ level: 'asc' }, { name: 'asc' }] },
        items: { orderBy: { name: 'asc' } },
        resources: true,
        conditions: true,
      },
    });
  }

  /**
   * Import ability scores
   */
  private async importAbilities(characterId: string, build: PathbuilderBuild) {
    const abilities = [
      { ability: AbilityType.STR, score: build.abilities.str },
      { ability: AbilityType.DEX, score: build.abilities.dex },
      { ability: AbilityType.CON, score: build.abilities.con },
      { ability: AbilityType.INT, score: build.abilities.int },
      { ability: AbilityType.WIS, score: build.abilities.wis },
      { ability: AbilityType.CHA, score: build.abilities.cha },
    ];

    await this.prisma.characterAbility.createMany({
      data: abilities.map(a => ({ ...a, characterId })),
    });

    this.logger.debug(`Imported ${abilities.length} abilities`);
  }

  /**
   * Import skills with proficiencies
   */
  private async importSkills(characterId: string, build: PathbuilderBuild) {
    const prof = build.proficiencies;
    const skills = [
      { skillName: 'Acrobatics', skillNameGerman: SKILL_TRANSLATIONS.acrobatics, proficiency: proficiencyFromValue(prof.acrobatics) },
      { skillName: 'Arcana', skillNameGerman: SKILL_TRANSLATIONS.arcana, proficiency: proficiencyFromValue(prof.arcana) },
      { skillName: 'Athletics', skillNameGerman: SKILL_TRANSLATIONS.athletics, proficiency: proficiencyFromValue(prof.athletics) },
      { skillName: 'Crafting', skillNameGerman: SKILL_TRANSLATIONS.crafting, proficiency: proficiencyFromValue(prof.crafting) },
      { skillName: 'Deception', skillNameGerman: SKILL_TRANSLATIONS.deception, proficiency: proficiencyFromValue(prof.deception) },
      { skillName: 'Diplomacy', skillNameGerman: SKILL_TRANSLATIONS.diplomacy, proficiency: proficiencyFromValue(prof.diplomacy) },
      { skillName: 'Intimidation', skillNameGerman: SKILL_TRANSLATIONS.intimidation, proficiency: proficiencyFromValue(prof.intimidation) },
      { skillName: 'Medicine', skillNameGerman: SKILL_TRANSLATIONS.medicine, proficiency: proficiencyFromValue(prof.medicine) },
      { skillName: 'Nature', skillNameGerman: SKILL_TRANSLATIONS.nature, proficiency: proficiencyFromValue(prof.nature) },
      { skillName: 'Occultism', skillNameGerman: SKILL_TRANSLATIONS.occultism, proficiency: proficiencyFromValue(prof.occultism) },
      { skillName: 'Performance', skillNameGerman: SKILL_TRANSLATIONS.performance, proficiency: proficiencyFromValue(prof.performance) },
      { skillName: 'Religion', skillNameGerman: SKILL_TRANSLATIONS.religion, proficiency: proficiencyFromValue(prof.religion) },
      { skillName: 'Society', skillNameGerman: SKILL_TRANSLATIONS.society, proficiency: proficiencyFromValue(prof.society) },
      { skillName: 'Stealth', skillNameGerman: SKILL_TRANSLATIONS.stealth, proficiency: proficiencyFromValue(prof.stealth) },
      { skillName: 'Survival', skillNameGerman: SKILL_TRANSLATIONS.survival, proficiency: proficiencyFromValue(prof.survival) },
      { skillName: 'Thievery', skillNameGerman: SKILL_TRANSLATIONS.thievery, proficiency: proficiencyFromValue(prof.thievery) },
      // Saves
      { skillName: 'Perception', skillNameGerman: 'Wahrnehmung', proficiency: proficiencyFromValue(prof.perception) },
      { skillName: 'Fortitude', skillNameGerman: 'Zähigkeit', proficiency: proficiencyFromValue(prof.fortitude) },
      { skillName: 'Reflex', skillNameGerman: 'Reflex', proficiency: proficiencyFromValue(prof.reflex) },
      { skillName: 'Will', skillNameGerman: 'Wille', proficiency: proficiencyFromValue(prof.will) },
    ];

    // Add lore skills
    for (const [loreName, profValue] of build.lores) {
      skills.push({
        skillName: `Lore: ${loreName}`,
        skillNameGerman: `Wissen: ${loreName}`,
        proficiency: proficiencyFromValue(profValue),
      });
    }

    await this.prisma.characterSkill.createMany({
      data: skills.map(s => ({
        characterId,
        skillName: s.skillName,
        proficiency: s.proficiency,
      })),
    });

    this.logger.debug(`Imported ${skills.length} skills`);
  }

  /**
   * Import feats with translations
   */
  private async importFeats(characterId: string, build: PathbuilderBuild) {
    if (!build.feats || build.feats.length === 0) {
      return;
    }

    // Extract feat names for batch translation
    const featNames = build.feats.map(f => ({ englishName: f[0] as string }));
    const translations = await this.translationsService.getTranslationsBatch(
      TranslationType.FEAT,
      featNames,
    );

    const feats = build.feats.map(feat => {
      const name = feat[0] as string;
      const featType = feat[2] as string;
      const level = feat[3] as number;
      const translation = translations.get(name);

      return {
        characterId,
        name,
        nameGerman: translation?.germanName || name,
        level,
        source: featSourceFromType(featType),
      };
    });

    await this.prisma.characterFeat.createMany({ data: feats });
    this.logger.debug(`Imported ${feats.length} feats`);
  }

  /**
   * Import items (weapons, armor, equipment)
   */
  private async importItems(characterId: string, build: PathbuilderBuild) {
    // Collect all raw item data from Pathbuilder
    const rawItems: Array<{
      name: string;
      quantity: number;
      equipped: boolean;
      invested: boolean;
      notes?: string;
      defaultBulk: number;
    }> = [];

    // Weapons
    for (const weapon of build.weapons || []) {
      rawItems.push({
        name: weapon.name,
        quantity: weapon.qty || 1,
        defaultBulk: 1,
        equipped: true,
        invested: false,
        notes: `${weapon.die} ${weapon.damageType}${weapon.extraDamage?.length ? ' + ' + weapon.extraDamage.join(', ') : ''}`,
      });
    }

    // Armor
    for (const armor of build.armor || []) {
      rawItems.push({
        name: armor.name,
        quantity: armor.qty || 1,
        defaultBulk: 1,
        equipped: armor.worn,
        invested: false,
      });
    }

    // General equipment
    for (const equip of build.equipment || []) {
      const name = equip[0] as string;
      const qty = equip[1] as number;
      const invested = equip[2] === 'Invested';

      rawItems.push({
        name,
        quantity: qty,
        defaultBulk: 0,
        equipped: false,
        invested,
      });
    }

    if (rawItems.length === 0) {
      return;
    }

    // Collect unique item names for database lookup
    const uniqueNames = [...new Set(rawItems.map(i => i.name))];

    // Find matching Equipment records in the database (case-insensitive)
    const equipmentRecords = await this.prisma.equipment.findMany({
      where: {
        OR: uniqueNames.map(name => ({
          name: { equals: name, mode: 'insensitive' as const },
        })),
      },
    });

    // Create a map for quick lookup (lowercase name -> equipment)
    const equipmentMap = new Map<string, typeof equipmentRecords[0]>();
    for (const eq of equipmentRecords) {
      equipmentMap.set(eq.name.toLowerCase(), eq);
    }

    // Log unmatched items for debugging
    for (const name of uniqueNames) {
      if (!equipmentMap.has(name.toLowerCase())) {
        this.logger.warn(`Item not found in equipment database: ${name}`);
      }
    }

    // Batch translate items (including summaries for caching)
    const translationRequests = uniqueNames.map(name => {
      const equipment = equipmentMap.get(name.toLowerCase());
      return {
        englishName: name,
        englishDescription: equipment?.summary || undefined,
      };
    });
    const translations = await this.translationsService.getTranslationsBatch(
      TranslationType.EQUIPMENT,
      translationRequests,
    );

    // Build final items with equipment linkage
    const items: Array<{
      characterId: string;
      equipmentId?: string;
      name: string;
      nameGerman?: string;
      quantity: number;
      bulk: number;
      equipped: boolean;
      invested: boolean;
      notes?: string;
    }> = [];

    for (const rawItem of rawItems) {
      const equipment = equipmentMap.get(rawItem.name.toLowerCase());
      const translation = translations.get(rawItem.name);

      // Parse bulk from equipment (can be "L" for light, number, or "-" for negligible)
      let bulk = rawItem.defaultBulk;
      if (equipment?.bulk) {
        if (equipment.bulk === 'L') {
          bulk = 0.1;
        } else if (equipment.bulk === '-') {
          bulk = 0;
        } else {
          const parsed = parseFloat(equipment.bulk);
          if (!isNaN(parsed)) {
            bulk = parsed;
          }
        }
      }

      items.push({
        characterId,
        equipmentId: equipment?.id,
        name: rawItem.name,
        nameGerman: translation?.germanName,
        quantity: rawItem.quantity,
        bulk,
        equipped: rawItem.equipped,
        invested: rawItem.invested,
        notes: rawItem.notes,
      });
    }

    if (items.length > 0) {
      await this.prisma.characterItem.createMany({
        data: items,
      });
      this.logger.debug(`Imported ${items.length} items (${equipmentRecords.length} matched with database)`);
    }
  }

  /**
   * Import resources (focus points, spell slots, etc.)
   */
  private async importResources(characterId: string, build: PathbuilderBuild) {
    const resources: Array<{ characterId: string; name: string; current: number; max: number }> = [];

    // Focus Points
    if (build.focusPoints > 0) {
      resources.push({
        characterId,
        name: 'Fokuspunkte',
        current: build.focusPoints,
        max: build.focusPoints,
      });
    }

    // Spell slots from spellcasters
    for (const caster of build.spellCasters || []) {
      if (caster.perDay) {
        for (let level = 0; level < caster.perDay.length; level++) {
          const slots = caster.perDay[level];
          if (slots > 0) {
            resources.push({
              characterId,
              name: level === 0 ? 'Zaubertricks' : `Zauberplätze Grad ${level}`,
              current: slots,
              max: slots,
            });
          }
        }
      }
    }

    // Hero Points (always starts at 1)
    resources.push({
      characterId,
      name: 'Heldenpunkte',
      current: 1,
      max: 3,
    });

    if (resources.length > 0) {
      await this.prisma.characterResource.createMany({ data: resources });
      this.logger.debug(`Imported ${resources.length} resources`);
    }
  }

  // ==========================================
  // ALCHEMY IMPORT
  // ==========================================

  /**
   * Check if character is an alchemist
   */
  private isAlchemist(build: PathbuilderBuild): boolean {
    return build.class.toLowerCase() === 'alchemist';
  }

  /**
   * Detect research field from character feats
   */
  private detectResearchField(build: PathbuilderBuild): ResearchField | null {
    if (!build.feats || build.feats.length === 0) {
      return null;
    }

    for (const feat of build.feats) {
      const featName = (feat[0] as string).toLowerCase();
      for (const [key, field] of Object.entries(RESEARCH_FIELD_FEATS)) {
        if (featName.includes(key)) {
          return field;
        }
      }
    }

    return null;
  }

  /**
   * Calculate versatile vials max based on level
   * Level 1-4: 2 vials + INT mod
   * Level 5-8: 2 vials + INT mod + 2
   * Level 9+: 2 vials + INT mod + 4 (and refills hourly)
   */
  private calculateVialsMax(build: PathbuilderBuild): number {
    const intMod = Math.floor((build.abilities.int - 10) / 2);
    const baseVials = 2 + intMod;

    if (build.level >= 9) {
      return baseVials + 4;
    } else if (build.level >= 5) {
      return baseVials + 2;
    }
    return baseVials;
  }

  /**
   * Calculate advanced alchemy batch size
   * Equal to INT modifier + level
   */
  private calculateAdvancedAlchemyMax(build: PathbuilderBuild): number {
    const intMod = Math.floor((build.abilities.int - 10) / 2);
    return Math.max(0, intMod + build.level);
  }

  /**
   * Import alchemy data (state and formulas)
   */
  private async importAlchemy(characterId: string, build: PathbuilderBuild) {
    // Create alchemy state
    const researchField = this.detectResearchField(build);
    const vialsMax = this.calculateVialsMax(build);
    const advancedAlchemyMax = this.calculateAdvancedAlchemyMax(build);

    await this.prisma.characterAlchemyState.create({
      data: {
        characterId,
        researchField,
        versatileVialsMax: vialsMax,
        versatileVialsCurrent: vialsMax,
        advancedAlchemyMax,
        advancedAlchemyBatch: 0,
      },
    });

    this.logger.log(`Created alchemy state: ${researchField || 'No research field'}, ${vialsMax} vials, ${advancedAlchemyMax} batch size`);

    // Import formulas
    await this.importFormulas(characterId, build);
  }

  /**
   * Import formulas from Pathbuilder data
   * Also pre-translates ALL versions of each formula (Lesser, Moderate, Greater, Major)
   */
  private async importFormulas(characterId: string, build: PathbuilderBuild) {
    if (!build.formula || build.formula.length === 0) {
      return;
    }

    const formulaNames: string[] = [];
    for (const formulaGroup of build.formula) {
      if (formulaGroup.known && formulaGroup.known.length > 0) {
        formulaNames.push(...formulaGroup.known);
      }
    }

    if (formulaNames.length === 0) {
      return;
    }

    // Find matching equipment in database
    const equipmentItems = await this.prisma.equipment.findMany({
      where: {
        name: { in: formulaNames },
      },
    });

    const equipmentMap = new Map(equipmentItems.map(e => [e.name.toLowerCase(), e]));

    // Translate formula names (including summaries AND effects for caching)
    const translationRequests = formulaNames.map(name => {
      const equipment = equipmentMap.get(name.toLowerCase());
      return {
        englishName: name,
        englishDescription: equipment?.summary || undefined,
        englishEffect: equipment?.effect || undefined,
      };
    });
    const translations = await this.translationsService.getTranslationsBatch(
      TranslationType.EQUIPMENT,
      translationRequests,
    );

    const formulas: Array<{
      characterId: string;
      equipmentId: string;
      name: string;
      nameGerman?: string;
      learnedAt: number;
      formulaSource: string;
    }> = [];

    for (const formulaName of formulaNames) {
      const equipment = equipmentMap.get(formulaName.toLowerCase());
      if (equipment) {
        const translation = translations.get(formulaName);
        formulas.push({
          characterId,
          equipmentId: equipment.id,
          name: formulaName,
          nameGerman: translation?.germanName,
          learnedAt: 1, // Default to level 1 from Pathbuilder
          formulaSource: 'Pathbuilder Import',
        });
      } else {
        this.logger.warn(`Formula not found in equipment database: ${formulaName}`);
      }
    }

    if (formulas.length > 0) {
      await this.prisma.characterFormula.createMany({
        data: formulas,
        skipDuplicates: true,
      });
      this.logger.debug(`Imported ${formulas.length} formulas`);

      // Pre-translate all versions of each formula in the background
      // This ensures fast loading when viewing available formulas later
      this.preTranslateAllFormulaVersions(formulaNames).catch(err => {
        this.logger.error('Failed to pre-translate formula versions:', err);
      });
    }
  }

  /**
   * Extract the base name from an alchemical item name
   * E.g., "Alchemist's Fire (Greater)" -> "Alchemist's Fire"
   *       "Lesser Elixir of Life" -> "Elixir of Life"
   */
  private extractBaseName(name: string): string {
    // Remove parenthetical suffix: "Item Name (Lesser)" -> "Item Name"
    let baseName = name.replace(/\s*\((Lesser|Minor|Moderate|Greater|Major|True)\)$/i, '').trim();
    // Also handle prefix style: "Lesser Item Name" -> "Item Name"
    baseName = baseName.replace(/^(Lesser|Minor|Moderate|Greater|Major|True)\s+/i, '').trim();
    return baseName;
  }

  /**
   * Check if an item name matches a base name (is a version of that item)
   */
  private isVersionOf(itemName: string, baseName: string): boolean {
    const itemBaseName = this.extractBaseName(itemName);
    return itemBaseName.toLowerCase() === baseName.toLowerCase();
  }

  /**
   * Pre-translate all versions of the given formulas (Lesser, Moderate, Greater, Major)
   */
  private async preTranslateAllFormulaVersions(formulaNames: string[]): Promise<void> {
    // Extract unique base names
    const baseNames = [...new Set(formulaNames.map(n => this.extractBaseName(n)))];

    // Find all alchemical items that are versions of these base formulas
    const allAlchemicalItems = await this.prisma.equipment.findMany({
      where: { itemCategory: 'Alchemical Items' },
    });

    const matchingVersions = allAlchemicalItems.filter(eq =>
      baseNames.some(baseName => this.isVersionOf(eq.name, baseName))
    );

    if (matchingVersions.length === 0) {
      return;
    }

    this.logger.debug(`Pre-translating ${matchingVersions.length} formula versions for ${baseNames.length} base formulas`);

    // Batch translate all versions (including summary AND effect)
    const itemsToTranslate = matchingVersions.map(eq => ({
      englishName: eq.name,
      englishDescription: eq.summary || undefined,
      englishEffect: eq.effect || undefined,
    }));

    await this.translationsService.getTranslationsBatch(
      TranslationType.EQUIPMENT,
      itemsToTranslate,
    );

    this.logger.debug(`Pre-translation complete for formula versions`);
  }
}
