import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateCombatantDto } from './dto';

@Injectable()
export class CombatantsService {
  constructor(private prisma: PrismaService) {}

  async findAll(campaignId: string, userId: string) {
    await this.verifyAccess(campaignId, userId);

    return this.prisma.combatant.findMany({
      where: { campaignId },
      include: { abilities: true },
      orderBy: [
        { level: 'asc' },
        { name: 'asc' },
      ],
    });
  }

  async findOne(campaignId: string, combatantId: string, userId: string) {
    await this.verifyAccess(campaignId, userId);

    const combatant = await this.prisma.combatant.findFirst({
      where: { id: combatantId, campaignId },
      include: { abilities: true },
    });

    if (!combatant) {
      throw new NotFoundException('Combatant not found');
    }

    return combatant;
  }

  async create(campaignId: string, dto: CreateCombatantDto, userId: string) {
    await this.verifyGMAccess(campaignId, userId);

    return this.prisma.combatant.create({
      data: {
        campaignId,
        name: dto.name,
        type: dto.type,
        level: dto.level,
        hpMax: dto.hpMax,
        ac: dto.ac,
        fortitude: dto.fortitude,
        reflex: dto.reflex,
        will: dto.will,
        perception: dto.perception,
        speed: dto.speed ?? 25,
        avatarUrl: dto.avatarUrl,
        description: dto.description,
        abilities: {
          create: dto.abilities?.map(ability => ({
            name: ability.name,
            actionCost: ability.actionCost,
            actionType: ability.actionType,
            description: ability.description,
            damage: ability.damage,
            traits: ability.traits ?? [],
          })) ?? [],
        },
      },
      include: { abilities: true },
    });
  }

  async update(campaignId: string, combatantId: string, dto: Partial<CreateCombatantDto>, userId: string) {
    await this.verifyGMAccess(campaignId, userId);

    const combatant = await this.prisma.combatant.findFirst({
      where: { id: combatantId, campaignId },
    });

    if (!combatant) {
      throw new NotFoundException('Combatant not found');
    }

    // Update combatant and replace abilities if provided
    return this.prisma.$transaction(async (tx) => {
      // Delete existing abilities if new ones are provided
      if (dto.abilities !== undefined) {
        await tx.combatantAbility.deleteMany({
          where: { combatantId },
        });
      }

      return tx.combatant.update({
        where: { id: combatantId },
        data: {
          name: dto.name,
          type: dto.type,
          level: dto.level,
          hpMax: dto.hpMax,
          ac: dto.ac,
          fortitude: dto.fortitude,
          reflex: dto.reflex,
          will: dto.will,
          perception: dto.perception,
          speed: dto.speed,
          avatarUrl: dto.avatarUrl,
          description: dto.description,
          abilities: dto.abilities ? {
            create: dto.abilities.map(ability => ({
              name: ability.name,
              actionCost: ability.actionCost,
              actionType: ability.actionType,
              description: ability.description,
              damage: ability.damage,
              traits: ability.traits ?? [],
            })),
          } : undefined,
        },
        include: { abilities: true },
      });
    });
  }

  async delete(campaignId: string, combatantId: string, userId: string) {
    await this.verifyGMAccess(campaignId, userId);

    const combatant = await this.prisma.combatant.findFirst({
      where: { id: combatantId, campaignId },
    });

    if (!combatant) {
      throw new NotFoundException('Combatant not found');
    }

    await this.prisma.combatant.delete({
      where: { id: combatantId },
    });

    return { message: 'Combatant deleted' };
  }

  // ==========================================
  // ACCESS HELPERS
  // ==========================================

  private async verifyAccess(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 isGM = campaign.gmId === userId;
    const isMember = campaign.members.some(m => m.userId === userId);

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

    return { campaign, isGM };
  }

  private async verifyGMAccess(campaignId: string, userId: string) {
    const campaign = await this.prisma.campaign.findUnique({
      where: { id: campaignId },
    });

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

    if (campaign.gmId !== userId) {
      throw new ForbiddenException('Only the GM can perform this action');
    }

    return campaign;
  }
}
