import {
  Injectable,
  NotFoundException,
  ForbiddenException,
  Inject,
  forwardRef,
} from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { TranslationsService } from '../translations/translations.service';
import { CharactersGateway } from './characters.gateway';
import { TranslationType } from '../../generated/prisma/client.js';
import {
  CreateCharacterDto,
  UpdateCharacterDto,
  CreateAbilityDto,
  CreateSkillDto,
  CreateFeatDto,
  CreateSpellDto,
  CreateItemDto,
  UpdateItemDto,
  CreateConditionDto,
  CreateResourceDto,
  RestPreviewDto,
  RestResultDto,
  ConditionReduced,
  RecoveryCheckResultDto,
  ApplyDamageResultDto,
  HealFromDyingResultDto,
} from './dto';

@Injectable()
export class CharactersService {
  constructor(
    private prisma: PrismaService,
    private translationsService: TranslationsService,
    @Inject(forwardRef(() => CharactersGateway))
    private charactersGateway: CharactersGateway,
  ) {}

  // Check if user has access to campaign
  private async checkCampaignAccess(campaignId: string, userId: string) {
    const campaign = await this.prisma.campaign.findUnique({
      where: { id: campaignId },
      include: { members: true },
    });

    if (!campaign) {
      throw new NotFoundException('Campaign not found');
    }

    const hasAccess =
      campaign.gmId === userId ||
      campaign.members.some((m) => m.userId === userId);

    if (!hasAccess) {
      throw new ForbiddenException('No access to this campaign');
    }

    return campaign;
  }

  // Check if user can edit character
  private async checkCharacterAccess(characterId: string, userId: string, requireOwnership = false) {
    const character = await this.prisma.character.findUnique({
      where: { id: characterId },
      include: { campaign: { include: { members: true } } },
    });

    if (!character) {
      throw new NotFoundException('Character not found');
    }

    const isGM = character.campaign.gmId === userId;
    const isOwner = character.ownerId === userId;

    if (requireOwnership && !isOwner && !isGM) {
      throw new ForbiddenException('Only the owner or GM can modify this character');
    }

    const isMember = character.campaign.members.some((m) => m.userId === userId);
    if (!isGM && !isMember) {
      throw new ForbiddenException('No access to this character');
    }

    return character;
  }

  async create(campaignId: string, dto: CreateCharacterDto, userId: string) {
    await this.checkCampaignAccess(campaignId, userId);

    return this.prisma.character.create({
      data: {
        ...dto,
        campaignId,
        ownerId: userId,
        pathbuilderData: dto.pathbuilderData as any,
      },
      include: {
        owner: { select: { id: true, username: true } },
        abilities: true,
        skills: true,
        feats: true,
        spells: true,
        items: true,
        conditions: true,
        resources: true,
      },
    });
  }

  async findAllByCampaign(campaignId: string, userId: string) {
    await this.checkCampaignAccess(campaignId, userId);

    return this.prisma.character.findMany({
      where: { campaignId },
      include: {
        owner: { select: { id: true, username: true } },
      },
      orderBy: { name: 'asc' },
    });
  }

  /**
   * Enrich equipment with German translations from the Translation table
   */
  private async enrichEquipmentWithTranslations(
    equipment: { name: string; summary?: string | null; effect?: string | null } | null | undefined,
  ): Promise<{ nameGerman?: string; summaryGerman?: string; effectGerman?: string }> {
    if (!equipment) {
      return {};
    }
    try {
      const translation = await this.translationsService.getTranslation(
        TranslationType.EQUIPMENT,
        equipment.name,
        equipment.summary || undefined,
        equipment.effect || undefined,
      );
      return {
        nameGerman: translation.germanName,
        summaryGerman: translation.germanDescription,
        effectGerman: translation.germanEffect,
      };
    } catch {
      return {};
    }
  }

  async findOne(id: string, userId: string) {
    const character = await this.checkCharacterAccess(id, userId);

    const fullCharacter = await this.prisma.character.findUnique({
      where: { id },
      include: {
        owner: { select: { id: true, username: true, avatarUrl: true } },
        abilities: true,
        skills: { orderBy: { skillName: 'asc' } },
        feats: { orderBy: [{ level: 'asc' }, { name: 'asc' }] },
        spells: { orderBy: [{ spellLevel: 'asc' }, { name: 'asc' }] },
        items: {
          orderBy: { name: 'asc' },
          include: {
            equipment: true,
          },
        },
        conditions: true,
        resources: true,
        // Alchemy
        alchemyState: true,
        formulas: {
          orderBy: [{ learnedAt: 'asc' }, { name: 'asc' }],
          include: { equipment: true },
        },
        preparedItems: {
          orderBy: { createdAt: 'desc' },
          include: { equipment: true },
        },
      },
    });

    if (!fullCharacter) {
      return null;
    }

    // Enrich items with equipment translations
    const enrichedItems = await Promise.all(
      fullCharacter.items.map(async (item) => {
        const translations = await this.enrichEquipmentWithTranslations(item.equipment);
        return {
          ...item,
          equipment: item.equipment
            ? { ...item.equipment, ...translations }
            : null,
        };
      }),
    );

    // Enrich formulas with equipment translations
    const enrichedFormulas = await Promise.all(
      fullCharacter.formulas.map(async (formula) => {
        const translations = await this.enrichEquipmentWithTranslations(formula.equipment);
        return {
          ...formula,
          equipment: formula.equipment
            ? { ...formula.equipment, ...translations }
            : null,
        };
      }),
    );

    // Enrich prepared items with equipment translations
    const enrichedPreparedItems = await Promise.all(
      fullCharacter.preparedItems.map(async (item) => {
        const translations = await this.enrichEquipmentWithTranslations(item.equipment);
        return {
          ...item,
          equipment: item.equipment
            ? { ...item.equipment, ...translations }
            : null,
        };
      }),
    );

    return {
      ...fullCharacter,
      items: enrichedItems,
      formulas: enrichedFormulas,
      preparedItems: enrichedPreparedItems,
    };
  }

  async update(id: string, dto: UpdateCharacterDto, userId: string) {
    await this.checkCharacterAccess(id, userId, true);

    return this.prisma.character.update({
      where: { id },
      data: {
        ...dto,
        pathbuilderData: dto.pathbuilderData as any,
      },
      include: {
        owner: { select: { id: true, username: true } },
        abilities: true,
        skills: true,
        feats: true,
        spells: true,
        items: true,
        conditions: true,
        resources: true,
      },
    });
  }

  async remove(id: string, userId: string) {
    await this.checkCharacterAccess(id, userId, true);

    await this.prisma.character.delete({ where: { id } });
    return { message: 'Character deleted successfully' };
  }

  // HP Management
  async updateHp(id: string, hpCurrent: number, hpTemp?: number, userId?: string) {
    console.log('[updateHp] Called with:', { id, hpCurrent, hpTemp, userId });

    if (userId) {
      await this.checkCharacterAccess(id, userId, true);
    }

    // Get current character state
    const character = await this.prisma.character.findUnique({
      where: { id },
      include: { conditions: true },
    });

    if (!character) {
      throw new NotFoundException('Character not found');
    }

    const oldHp = character.hpCurrent;
    const newHp = Math.max(0, hpCurrent);
    const droppedToZero = oldHp > 0 && newHp === 0;

    console.log('[updateHp] HP change:', { oldHp, newHp, droppedToZero });

    // Update HP
    const result = await this.prisma.character.update({
      where: { id },
      data: {
        hpCurrent: newHp,
        ...(hpTemp !== undefined && { hpTemp: Math.max(0, hpTemp) }),
      },
    });

    // Broadcast HP update
    this.charactersGateway.broadcastCharacterUpdate(id, {
      characterId: id,
      type: 'hp',
      data: { hpCurrent: result.hpCurrent, hpTemp: result.hpTemp, hpMax: result.hpMax },
    });

    // If HP dropped to 0, add Dying condition
    if (droppedToZero) {
      console.log('[updateHp] Character dropped to 0 HP, adding Dying condition');
      const { dying, wounded, doomed } = await this.getDyingConditions(id);
      const deathThreshold = this.getDeathThreshold(doomed?.value || null);

      // Calculate dying value: base 1 + wounded value
      const woundedBonus = wounded?.value || 0;
      let dyingValue = 1 + woundedBonus;
      console.log('[updateHp] Dying value:', { dyingValue, woundedBonus, deathThreshold });

      // Check for instant death
      const died = dyingValue >= deathThreshold;
      if (died) {
        dyingValue = deathThreshold;
      }

      // Add or update Dying condition
      if (dying) {
        await this.prisma.characterCondition.update({
          where: { id: dying.id },
          data: { value: dyingValue },
        });
      } else {
        await this.prisma.characterCondition.create({
          data: {
            characterId: id,
            name: 'Dying',
            nameGerman: 'Sterbend',
            value: dyingValue,
          },
        });
      }

      // Broadcast dying update
      const updatedConditions = await this.prisma.characterCondition.findMany({
        where: { characterId: id },
      });

      this.charactersGateway.broadcastCharacterUpdate(id, {
        characterId: id,
        type: 'dying',
        data: {
          action: 'entered_dying',
          dyingValue,
          died,
          conditions: updatedConditions,
        },
      });

      // Also broadcast conditions update for UI sync
      this.charactersGateway.broadcastCharacterUpdate(id, {
        characterId: id,
        type: 'conditions',
        data: {
          action: dying ? 'update' : 'add',
          condition: updatedConditions.find(c => c.name === 'Dying'),
        },
      });
    }

    // If HP increased from 0 and character was dying, handle recovery
    if (oldHp === 0 && newHp > 0) {
      const { dying, wounded } = await this.getDyingConditions(id);

      if (dying) {
        // Remove Dying condition
        await this.prisma.characterCondition.delete({ where: { id: dying.id } });

        // Add or increment Wounded
        let newWoundedValue = 1;
        if (wounded) {
          newWoundedValue = (wounded.value || 0) + 1;
          await this.prisma.characterCondition.update({
            where: { id: wounded.id },
            data: { value: newWoundedValue },
          });
        } else {
          await this.prisma.characterCondition.create({
            data: {
              characterId: id,
              name: 'Wounded',
              nameGerman: 'Verwundet',
              value: 1,
            },
          });
        }

        // Broadcast dying update
        const updatedConditions = await this.prisma.characterCondition.findMany({
          where: { characterId: id },
        });

        this.charactersGateway.broadcastCharacterUpdate(id, {
          characterId: id,
          type: 'dying',
          data: {
            action: 'healed_from_dying',
            woundedValue: newWoundedValue,
            conditions: updatedConditions,
          },
        });
      }
    }

    return result;
  }

  // Credits Management
  async updateCredits(id: string, credits: number, userId?: string) {
    if (userId) {
      await this.checkCharacterAccess(id, userId, true);
    }

    const result = await this.prisma.character.update({
      where: { id },
      data: {
        credits: Math.max(0, credits),
      },
    });

    // Broadcast money update
    this.charactersGateway.broadcastCharacterUpdate(id, {
      characterId: id,
      type: 'money',
      data: { credits: result.credits },
    });

    return result;
  }

  // Abilities
  async setAbilities(characterId: string, abilities: CreateAbilityDto[], userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    // Delete existing and create new
    await this.prisma.characterAbility.deleteMany({ where: { characterId } });

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

  // Skills
  async setSkills(characterId: string, skills: CreateSkillDto[], userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    await this.prisma.characterSkill.deleteMany({ where: { characterId } });

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

  async updateSkill(characterId: string, skillName: string, proficiency: string, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    return this.prisma.characterSkill.upsert({
      where: { characterId_skillName: { characterId, skillName } },
      update: { proficiency: proficiency as any },
      create: { characterId, skillName, proficiency: proficiency as any },
    });
  }

  // Feats
  async addFeat(characterId: string, dto: CreateFeatDto, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    let nameGerman = dto.nameGerman;

    // If featId is provided, translate and update the Feat record
    if (dto.featId) {
      const feat = await this.prisma.feat.findUnique({
        where: { id: dto.featId },
      });

      if (feat) {
        // Check if feat needs translation
        if (!feat.nameGerman || !feat.summaryGerman) {
          const translation = await this.translationsService.getTranslation(
            TranslationType.FEAT,
            feat.name,
            feat.summary || undefined,
          );

          // Update the Feat record with translations
          await this.prisma.feat.update({
            where: { id: dto.featId },
            data: {
              nameGerman: feat.nameGerman || translation.germanName,
              summaryGerman: feat.summaryGerman || translation.germanDescription,
            },
          });

          nameGerman = nameGerman || translation.germanName;
        } else {
          nameGerman = nameGerman || feat.nameGerman;
        }
      }
    }

    // Fallback: translate just the name if still missing
    if (!nameGerman) {
      const translation = await this.translationsService.getTranslation(
        TranslationType.FEAT,
        dto.name,
      );
      nameGerman = translation.germanName;
    }

    return this.prisma.characterFeat.create({
      data: {
        ...dto,
        nameGerman,
        characterId,
      },
    });
  }

  async removeFeat(characterId: string, featId: string, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    await this.prisma.characterFeat.delete({ where: { id: featId } });
    return { message: 'Feat removed' };
  }

  // Spells
  async addSpell(characterId: string, dto: CreateSpellDto, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    return this.prisma.characterSpell.create({
      data: { ...dto, characterId },
    });
  }

  async updateSpell(characterId: string, spellId: string, prepared: boolean, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    return this.prisma.characterSpell.update({
      where: { id: spellId },
      data: { prepared },
    });
  }

  async removeSpell(characterId: string, spellId: string, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    await this.prisma.characterSpell.delete({ where: { id: spellId } });
    return { message: 'Spell removed' };
  }

  // Items
  async addItem(characterId: string, dto: CreateItemDto, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    // Hole Equipment-Details für die Summary (falls equipmentId vorhanden)
    let summary: string | undefined;
    if (dto.equipmentId) {
      const equipment = await this.prisma.equipment.findUnique({
        where: { id: dto.equipmentId },
        select: { summary: true },
      });
      summary = equipment?.summary || undefined;
    }

    // Übersetze den Item-Namen (und Summary), falls noch nicht vorhanden
    let nameGerman = dto.nameGerman;
    if (!nameGerman && dto.name) {
      const translation = await this.translationsService.getTranslation(
        TranslationType.EQUIPMENT,
        dto.name,
        summary, // Summary wird mit übersetzt
      );
      nameGerman = translation.germanName;
    }

    const result = await this.prisma.characterItem.create({
      data: { ...dto, characterId, bulk: dto.bulk as any, nameGerman },
      include: { equipment: true },
    });

    // Broadcast inventory update
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'inventory',
      data: { action: 'add', item: result },
    });

    return result;
  }

  async updateItem(characterId: string, itemId: string, data: UpdateItemDto, userId: string) {
    const character = await this.prisma.character.findUnique({
      where: { id: characterId },
      include: { campaign: true },
    });

    if (!character) {
      throw new NotFoundException('Character not found');
    }

    const isGM = character.campaign.gmId === userId;
    const isOwner = character.ownerId === userId;

    if (!isOwner && !isGM) {
      throw new ForbiddenException('Only the owner or GM can modify this character');
    }

    // Separate player-allowed fields from GM-only fields
    const playerFields = {
      quantity: data.quantity,
      equipped: data.equipped,
      invested: data.invested,
      notes: data.notes,
      alias: data.alias,
    };

    const gmFields = {
      customName: data.customName,
      customDamage: data.customDamage,
      customDamageType: data.customDamageType,
      customTraits: data.customTraits,
      customRange: data.customRange,
      customHands: data.customHands,
    };

    // Build update data based on role
    const updateData: any = {};

    // Players can always update player fields
    Object.entries(playerFields).forEach(([key, value]) => {
      if (value !== undefined) {
        updateData[key] = value;
      }
    });

    // Only GM can update GM fields
    if (isGM) {
      Object.entries(gmFields).forEach(([key, value]) => {
        if (value !== undefined) {
          updateData[key] = value;
        }
      });
    }

    const result = await this.prisma.characterItem.update({
      where: { id: itemId },
      data: updateData,
      include: { equipment: true },
    });

    // Broadcast item update (could be equipment status change or other update)
    const updateType = data.equipped !== undefined ? 'equipment_status' : 'item';
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: updateType,
      data: { action: 'update', item: result },
    });

    return result;
  }

  async removeItem(characterId: string, itemId: string, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    await this.prisma.characterItem.delete({ where: { id: itemId } });

    // Broadcast inventory update
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'inventory',
      data: { action: 'remove', itemId },
    });

    return { message: 'Item removed' };
  }

  // Conditions
  async addCondition(characterId: string, dto: CreateConditionDto, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    // Übersetze den Condition-Namen, falls noch nicht vorhanden
    let nameGerman = dto.nameGerman;
    if (!nameGerman && dto.name) {
      const translation = await this.translationsService.getTranslation(
        TranslationType.CONDITION,
        dto.name,
      );
      nameGerman = translation.germanName;
    }

    const result = await this.prisma.characterCondition.create({
      data: { ...dto, characterId, nameGerman },
    });

    // Broadcast conditions update
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'conditions',
      data: { action: 'add', condition: result },
    });

    return result;
  }

  async updateCondition(characterId: string, conditionId: string, value: number, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    const result = await this.prisma.characterCondition.update({
      where: { id: conditionId },
      data: { value },
    });

    // Broadcast conditions update
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'conditions',
      data: { action: 'update', condition: result },
    });

    return result;
  }

  async removeCondition(characterId: string, conditionId: string, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    await this.prisma.characterCondition.delete({ where: { id: conditionId } });

    // Broadcast conditions update
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'conditions',
      data: { action: 'remove', conditionId },
    });

    return { message: 'Condition removed' };
  }

  // Resources
  async setResources(characterId: string, resources: CreateResourceDto[], userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    await this.prisma.characterResource.deleteMany({ where: { characterId } });

    return this.prisma.characterResource.createMany({
      data: resources.map((r) => ({ ...r, characterId })),
    });
  }

  async updateResource(characterId: string, resourceName: string, current: number, userId: string) {
    await this.checkCharacterAccess(characterId, userId, true);

    return this.prisma.characterResource.update({
      where: { characterId_name: { characterId, name: resourceName } },
      data: { current: Math.max(0, current) },
    });
  }

  // ==========================================
  // REST SYSTEM
  // ==========================================

  /**
   * Preview what will happen when the character rests
   * PF2e Rest Rules:
   * - HP healing: CON modifier × level (minimum 1 × level)
   * - Fatigued condition is removed
   * - Doomed condition is reduced by 1
   * - Drained condition is reduced by 1
   * - All resources are reset to max (spell slots, focus points, etc.)
   * - Alchemy: Infused items expire, vials refill
   */
  async getRestPreview(characterId: string, userId: string): Promise<RestPreviewDto> {
    const character = await this.checkCharacterAccess(characterId, userId);

    // Load all character data
    const fullCharacter = await this.prisma.character.findUnique({
      where: { id: characterId },
      include: {
        abilities: true,
        conditions: true,
        resources: true,
        alchemyState: true,
        preparedItems: true,
      },
    });

    if (!fullCharacter) {
      throw new NotFoundException('Character not found');
    }

    // Calculate CON modifier
    const conAbility = fullCharacter.abilities.find((a) => a.ability === 'CON');
    const conScore = conAbility?.score || 10;
    const conMod = Math.floor((conScore - 10) / 2);

    // HP healing: CON mod × level (min 1 × level)
    const hpToHeal = Math.max(1, conMod) * fullCharacter.level;
    const hpAfterRest = Math.min(fullCharacter.hpMax, fullCharacter.hpCurrent + hpToHeal);

    // Conditions to remove (Fatigued)
    const conditionsToRemove: string[] = [];
    const fatigued = fullCharacter.conditions.find(
      (c) => c.name.toLowerCase() === 'fatigued' || c.nameGerman?.toLowerCase() === 'erschöpft',
    );
    if (fatigued) {
      conditionsToRemove.push(fatigued.nameGerman || fatigued.name);
    }

    // Conditions to reduce (Doomed, Drained)
    const conditionsToReduce: ConditionReduced[] = [];
    for (const condition of fullCharacter.conditions) {
      const nameLower = condition.name.toLowerCase();
      const isDoomed = nameLower === 'doomed' || condition.nameGerman?.toLowerCase() === 'verdammt';
      const isDrained = nameLower === 'drained' || condition.nameGerman?.toLowerCase() === 'entkräftet';

      if ((isDoomed || isDrained) && condition.value && condition.value > 0) {
        conditionsToReduce.push({
          name: condition.nameGerman || condition.name,
          oldValue: condition.value,
          newValue: condition.value - 1,
        });
      }
    }

    // Resources to reset
    const resourcesToReset: string[] = fullCharacter.resources
      .filter((r) => r.current < r.max)
      .map((r) => r.name);

    // Check if character has alchemy to reset
    const alchemyReset = !!fullCharacter.alchemyState;

    // Count infused items that will expire (only items with isInfused=true)
    const infusedItemsCount = fullCharacter.preparedItems
      .filter((item) => item.isInfused)
      .reduce((sum, item) => sum + item.quantity, 0);

    return {
      hpToHeal: hpAfterRest - fullCharacter.hpCurrent,
      hpAfterRest,
      conditionsToRemove,
      conditionsToReduce,
      resourcesToReset,
      alchemyReset,
      infusedItemsCount,
    };
  }

  /**
   * Perform a full rest for the character
   */
  async performRest(characterId: string, userId: string): Promise<RestResultDto> {
    const character = await this.checkCharacterAccess(characterId, userId, true);

    // Load all character data
    const fullCharacter = await this.prisma.character.findUnique({
      where: { id: characterId },
      include: {
        abilities: true,
        conditions: true,
        resources: true,
        alchemyState: true,
        preparedItems: true,
      },
    });

    if (!fullCharacter) {
      throw new NotFoundException('Character not found');
    }

    // Calculate CON modifier
    const conAbility = fullCharacter.abilities.find((a) => a.ability === 'CON');
    const conScore = conAbility?.score || 10;
    const conMod = Math.floor((conScore - 10) / 2);

    // HP healing
    const hpToHeal = Math.max(1, conMod) * fullCharacter.level;
    const newHpCurrent = Math.min(fullCharacter.hpMax, fullCharacter.hpCurrent + hpToHeal);
    const hpHealed = newHpCurrent - fullCharacter.hpCurrent;

    // Track what happened
    const conditionsRemoved: string[] = [];
    const conditionsReduced: ConditionReduced[] = [];
    const resourcesReset: string[] = [];

    // Process conditions
    for (const condition of fullCharacter.conditions) {
      const nameLower = condition.name.toLowerCase();
      const isFatigued =
        nameLower === 'fatigued' || condition.nameGerman?.toLowerCase() === 'erschöpft';
      const isDoomed = nameLower === 'doomed' || condition.nameGerman?.toLowerCase() === 'verdammt';
      const isDrained =
        nameLower === 'drained' || condition.nameGerman?.toLowerCase() === 'entkräftet';

      if (isFatigued) {
        // Remove Fatigued
        await this.prisma.characterCondition.delete({ where: { id: condition.id } });
        conditionsRemoved.push(condition.nameGerman || condition.name);
      } else if ((isDoomed || isDrained) && condition.value && condition.value > 0) {
        const newValue = condition.value - 1;
        if (newValue <= 0) {
          // Remove if reduced to 0
          await this.prisma.characterCondition.delete({ where: { id: condition.id } });
          conditionsRemoved.push(condition.nameGerman || condition.name);
        } else {
          // Reduce by 1
          await this.prisma.characterCondition.update({
            where: { id: condition.id },
            data: { value: newValue },
          });
          conditionsReduced.push({
            name: condition.nameGerman || condition.name,
            oldValue: condition.value,
            newValue,
          });
        }
      }
    }

    // Reset resources to max
    for (const resource of fullCharacter.resources) {
      if (resource.current < resource.max) {
        await this.prisma.characterResource.update({
          where: { id: resource.id },
          data: { current: resource.max },
        });
        resourcesReset.push(resource.name);
      }
    }

    // Update HP
    await this.prisma.character.update({
      where: { id: characterId },
      data: { hpCurrent: newHpCurrent },
    });

    // Reset alchemy if present
    let alchemyReset = false;
    if (fullCharacter.alchemyState) {
      // Delete only infused items (non-infused/permanent items remain)
      await this.prisma.characterPreparedItem.deleteMany({
        where: { characterId, isInfused: true },
      });

      // Reset vials to max and reset batch counter
      await this.prisma.characterAlchemyState.update({
        where: { characterId },
        data: {
          versatileVialsCurrent: fullCharacter.alchemyState.versatileVialsMax,
          advancedAlchemyBatch: 0,
          lastRestAt: new Date(),
        },
      });

      alchemyReset = true;
    }

    // Broadcast the rest event
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'rest',
      data: {
        hpCurrent: newHpCurrent,
        hpHealed,
        conditionsRemoved,
        conditionsReduced,
        resourcesReset,
        alchemyReset,
      },
    });

    return {
      hpHealed,
      hpCurrent: newHpCurrent,
      conditionsRemoved,
      conditionsReduced,
      resourcesReset,
      alchemyReset,
    };
  }

  // ==========================================
  // DYING SYSTEM (PF2e Death & Dying)
  // ==========================================

  /**
   * Get the current dying-related conditions for a character
   */
  private async getDyingConditions(characterId: string) {
    const conditions = await this.prisma.characterCondition.findMany({
      where: { characterId },
    });

    const dying = conditions.find(
      (c) => c.name.toLowerCase() === 'dying' || c.nameGerman?.toLowerCase() === 'sterbend',
    );
    const wounded = conditions.find(
      (c) => c.name.toLowerCase() === 'wounded' || c.nameGerman?.toLowerCase() === 'verwundet',
    );
    const doomed = conditions.find(
      (c) => c.name.toLowerCase() === 'doomed' || c.nameGerman?.toLowerCase() === 'verdammt',
    );

    return { dying, wounded, doomed, all: conditions };
  }

  /**
   * Get the death threshold (4 - doomed value)
   */
  private getDeathThreshold(doomedValue: number | null): number {
    return 4 - (doomedValue || 0);
  }

  /**
   * Apply damage to a character, handling dying mechanics when HP reaches 0
   * PF2e Rules:
   * - When HP reaches 0, gain Dying 1 (Dying 2 if critical hit)
   * - Add Wounded value to Dying when going unconscious
   * - Death occurs at Dying 4 (or lower with Doomed)
   */
  async applyDamage(
    characterId: string,
    damage: number,
    isCritical = false,
    userId?: string,
  ): Promise<ApplyDamageResultDto> {
    if (userId) {
      await this.checkCharacterAccess(characterId, userId, true);
    }

    const character = await this.prisma.character.findUnique({
      where: { id: characterId },
      include: { conditions: true },
    });

    if (!character) {
      throw new NotFoundException('Character not found');
    }

    const { dying, wounded, doomed } = await this.getDyingConditions(characterId);
    const deathThreshold = this.getDeathThreshold(doomed?.value || null);

    // Calculate new HP
    let newHp = character.hpCurrent - damage;
    const droppedToZero = character.hpCurrent > 0 && newHp <= 0;
    newHp = Math.max(0, newHp);

    // Update HP
    await this.prisma.character.update({
      where: { id: characterId },
      data: { hpCurrent: newHp },
    });

    let dyingValue: number | null = dying?.value || null;
    let died = false;
    let message = `${damage} Schaden erhalten.`;

    if (droppedToZero) {
      // Character dropped to 0 HP - enter dying state
      const baseDying = isCritical ? 2 : 1;
      const woundedBonus = wounded?.value || 0;
      dyingValue = baseDying + woundedBonus;

      // Check for instant death
      if (dyingValue >= deathThreshold) {
        died = true;
        dyingValue = deathThreshold;
        message = `${damage} Schaden! ${character.name} ist tot.`;
      } else {
        message = `${damage} Schaden! ${character.name} wird bewusstlos mit Sterbend ${dyingValue}.`;
      }

      // Add or update Dying condition
      if (dying) {
        await this.prisma.characterCondition.update({
          where: { id: dying.id },
          data: { value: dyingValue },
        });
      } else {
        await this.prisma.characterCondition.create({
          data: {
            characterId,
            name: 'Dying',
            nameGerman: 'Sterbend',
            value: dyingValue,
          },
        });
      }

      // Broadcast conditions update
      const updatedConditions = await this.prisma.characterCondition.findMany({
        where: { characterId },
      });
      this.charactersGateway.broadcastCharacterUpdate(characterId, {
        characterId,
        type: 'dying',
        data: {
          action: 'entered_dying',
          dyingValue,
          died,
          conditions: updatedConditions,
        },
      });
    } else if (dying && newHp === 0) {
      // Already dying, taking damage while at 0 HP increases Dying by 1 (2 if crit)
      const increase = isCritical ? 2 : 1;
      dyingValue = Math.min((dying.value || 0) + increase, deathThreshold);

      if (dyingValue >= deathThreshold) {
        died = true;
        message = `${damage} Schaden! ${character.name} ist tot.`;
      } else {
        message = `${damage} Schaden! Sterbend erhöht auf ${dyingValue}.`;
      }

      await this.prisma.characterCondition.update({
        where: { id: dying.id },
        data: { value: dyingValue },
      });

      this.charactersGateway.broadcastCharacterUpdate(characterId, {
        characterId,
        type: 'dying',
        data: {
          action: 'dying_increased',
          dyingValue,
          died,
        },
      });
    }

    // Broadcast HP update
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'hp',
      data: { hpCurrent: newHp, hpTemp: character.hpTemp, hpMax: character.hpMax },
    });

    return {
      newHp,
      dyingValue,
      died,
      message,
    };
  }

  /**
   * Perform a recovery check for a dying character
   * PF2e Rules:
   * - DC = 10 + current Dying value
   * - Critical Success: Reduce Dying by 2
   * - Success: Reduce Dying by 1
   * - Failure: Increase Dying by 1
   * - Critical Failure: Increase Dying by 2
   * - At Dying 0, become stable (unconscious) and gain Wounded 1 (or +1)
   * - At Dying 4+ (or lower with Doomed), character dies
   */
  async performRecoveryCheck(
    characterId: string,
    dieRoll: number,
    userId?: string,
  ): Promise<RecoveryCheckResultDto> {
    if (userId) {
      await this.checkCharacterAccess(characterId, userId, true);
    }

    const { dying, wounded, doomed } = await this.getDyingConditions(characterId);

    if (!dying || !dying.value || dying.value <= 0) {
      throw new ForbiddenException('Character is not dying');
    }

    const dc = 10 + dying.value;
    const deathThreshold = this.getDeathThreshold(doomed?.value || null);

    // Determine result
    let result: 'critical_success' | 'success' | 'failure' | 'critical_failure';
    let dyingChange: number;

    if (dieRoll === 20 || dieRoll >= dc + 10) {
      result = 'critical_success';
      dyingChange = -2;
    } else if (dieRoll >= dc) {
      result = 'success';
      dyingChange = -1;
    } else if (dieRoll === 1 || dieRoll <= dc - 10) {
      result = 'critical_failure';
      dyingChange = 2;
    } else {
      result = 'failure';
      dyingChange = 1;
    }

    const newDyingValue = Math.max(0, (dying.value || 0) + dyingChange);
    const stabilized = newDyingValue <= 0;
    const died = newDyingValue >= deathThreshold;
    let woundedAdded = false;

    // Generate message
    let message: string;
    const resultNames = {
      critical_success: 'Kritischer Erfolg',
      success: 'Erfolg',
      failure: 'Fehlschlag',
      critical_failure: 'Kritischer Fehlschlag',
    };

    if (died) {
      message = `${resultNames[result]}! Sterbend erreicht ${newDyingValue}. Der Charakter ist tot.`;
    } else if (stabilized) {
      message = `${resultNames[result]}! Der Charakter ist stabil und bewusstlos.`;
    } else {
      message = `${resultNames[result]}! Sterbend ${dying.value} → ${newDyingValue}.`;
    }

    // Update Dying condition
    if (stabilized) {
      // Remove Dying
      await this.prisma.characterCondition.delete({ where: { id: dying.id } });

      // Add or increment Wounded
      if (wounded) {
        await this.prisma.characterCondition.update({
          where: { id: wounded.id },
          data: { value: (wounded.value || 0) + 1 },
        });
      } else {
        await this.prisma.characterCondition.create({
          data: {
            characterId,
            name: 'Wounded',
            nameGerman: 'Verwundet',
            value: 1,
          },
        });
      }
      woundedAdded = true;
    } else {
      // Update Dying value
      await this.prisma.characterCondition.update({
        where: { id: dying.id },
        data: { value: Math.min(newDyingValue, deathThreshold) },
      });
    }

    // Broadcast update
    const updatedConditions = await this.prisma.characterCondition.findMany({
      where: { characterId },
    });

    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'dying',
      data: {
        action: 'recovery_check',
        result,
        dieRoll,
        dc,
        newDyingValue: stabilized ? 0 : Math.min(newDyingValue, deathThreshold),
        stabilized,
        died,
        woundedAdded,
        conditions: updatedConditions,
      },
    });

    return {
      result,
      dieRoll,
      dc,
      newDyingValue: stabilized ? 0 : Math.min(newDyingValue, deathThreshold),
      stabilized,
      died,
      message,
      woundedAdded,
    };
  }

  /**
   * Heal a character who is dying
   * PF2e Rules:
   * - When healed from 0 HP, remove Dying and gain Wounded 1 (or +1)
   */
  async healFromDying(
    characterId: string,
    healAmount: number,
    userId?: string,
  ): Promise<HealFromDyingResultDto> {
    if (userId) {
      await this.checkCharacterAccess(characterId, userId, true);
    }

    const character = await this.prisma.character.findUnique({
      where: { id: characterId },
    });

    if (!character) {
      throw new NotFoundException('Character not found');
    }

    const { dying, wounded } = await this.getDyingConditions(characterId);

    // Calculate new HP
    const newHp = Math.min(character.hpMax, character.hpCurrent + healAmount);

    // Update HP
    await this.prisma.character.update({
      where: { id: characterId },
      data: { hpCurrent: newHp },
    });

    let woundedValue = wounded?.value || 0;
    let message = `${healAmount} HP geheilt. HP: ${newHp}.`;

    // If character was dying, remove Dying and add Wounded
    if (dying) {
      await this.prisma.characterCondition.delete({ where: { id: dying.id } });

      if (wounded) {
        woundedValue = (wounded.value || 0) + 1;
        await this.prisma.characterCondition.update({
          where: { id: wounded.id },
          data: { value: woundedValue },
        });
      } else {
        woundedValue = 1;
        await this.prisma.characterCondition.create({
          data: {
            characterId,
            name: 'Wounded',
            nameGerman: 'Verwundet',
            value: 1,
          },
        });
      }

      message = `${healAmount} HP geheilt! Sterbend entfernt, Verwundet ${woundedValue}.`;

      // Broadcast dying update
      const updatedConditions = await this.prisma.characterCondition.findMany({
        where: { characterId },
      });

      this.charactersGateway.broadcastCharacterUpdate(characterId, {
        characterId,
        type: 'dying',
        data: {
          action: 'healed_from_dying',
          woundedValue,
          conditions: updatedConditions,
        },
      });
    }

    // Broadcast HP update
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'hp',
      data: { hpCurrent: newHp, hpTemp: character.hpTemp, hpMax: character.hpMax },
    });

    return {
      newHp,
      woundedValue,
      message,
    };
  }

  /**
   * Get current dying state for a character
   */
  async getDyingState(characterId: string, userId?: string) {
    if (userId) {
      await this.checkCharacterAccess(characterId, userId);
    }

    const { dying, wounded, doomed } = await this.getDyingConditions(characterId);
    const deathThreshold = this.getDeathThreshold(doomed?.value || null);

    return {
      isDying: !!dying && (dying.value || 0) > 0,
      dyingValue: dying?.value || 0,
      woundedValue: wounded?.value || 0,
      doomedValue: doomed?.value || 0,
      deathThreshold,
      recoveryDC: dying ? 10 + (dying.value || 0) : null,
    };
  }

  /**
   * Revive a dead character
   * Removes Dying condition, sets HP to 1, and optionally clears Wounded/Doomed
   */
  async reviveCharacter(characterId: string, userId?: string) {
    if (userId) {
      await this.checkCharacterAccess(characterId, userId, true);
    }

    const character = await this.prisma.character.findUnique({
      where: { id: characterId },
    });

    if (!character) {
      throw new NotFoundException('Character not found');
    }

    const { dying, wounded, doomed } = await this.getDyingConditions(characterId);

    // Remove Dying condition
    if (dying) {
      await this.prisma.characterCondition.delete({ where: { id: dying.id } });
    }

    // Remove Wounded condition (fresh start after resurrection)
    if (wounded) {
      await this.prisma.characterCondition.delete({ where: { id: wounded.id } });
    }

    // Remove Doomed condition
    if (doomed) {
      await this.prisma.characterCondition.delete({ where: { id: doomed.id } });
    }

    // Set HP to 1
    await this.prisma.character.update({
      where: { id: characterId },
      data: { hpCurrent: 1 },
    });

    // Broadcast HP update
    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'hp',
      data: { hpCurrent: 1, hpTemp: character.hpTemp, hpMax: character.hpMax },
    });

    // Broadcast conditions update
    const updatedConditions = await this.prisma.characterCondition.findMany({
      where: { characterId },
    });

    this.charactersGateway.broadcastCharacterUpdate(characterId, {
      characterId,
      type: 'dying',
      data: {
        action: 'revived',
        conditions: updatedConditions,
      },
    });

    return { success: true, message: 'Charakter wiederbelebt' };
  }
}
