import {
  Injectable,
  NotFoundException,
  ForbiddenException,
  ConflictException,
} from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateCampaignDto, UpdateCampaignDto, AddMemberDto } from './dto';
import { UserRole } from '../../generated/prisma/client.js';

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

  async create(dto: CreateCampaignDto, userId: string) {
    // Create campaign with user as GM
    const campaign = await this.prisma.campaign.create({
      data: {
        name: dto.name,
        description: dto.description,
        imageUrl: dto.imageUrl,
        gmId: userId,
        members: {
          create: {
            userId: userId,
          },
        },
      },
      include: {
        gm: {
          select: {
            id: true,
            username: true,
            avatarUrl: true,
          },
        },
        members: {
          include: {
            user: {
              select: {
                id: true,
                username: true,
                avatarUrl: true,
              },
            },
          },
        },
        _count: {
          select: {
            characters: true,
          },
        },
      },
    });

    return campaign;
  }

  async findAll(userId: string) {
    // Get all campaigns where user is GM or member
    const campaigns = await this.prisma.campaign.findMany({
      where: {
        OR: [
          { gmId: userId },
          { members: { some: { userId } } },
        ],
      },
      include: {
        gm: {
          select: {
            id: true,
            username: true,
            avatarUrl: true,
          },
        },
        members: {
          include: {
            user: {
              select: {
                id: true,
                username: true,
                avatarUrl: true,
              },
            },
          },
        },
        _count: {
          select: {
            characters: true,
          },
        },
      },
      orderBy: {
        updatedAt: 'desc',
      },
    });

    return campaigns;
  }

  async findOne(id: string, userId: string) {
    const campaign = await this.prisma.campaign.findUnique({
      where: { id },
      include: {
        gm: {
          select: {
            id: true,
            username: true,
            email: true,
            avatarUrl: true,
          },
        },
        members: {
          include: {
            user: {
              select: {
                id: true,
                username: true,
                email: true,
                avatarUrl: true,
              },
            },
          },
        },
        characters: {
          select: {
            id: true,
            name: true,
            type: true,
            level: true,
            avatarUrl: true,
            hpCurrent: true,
            hpMax: true,
            owner: {
              select: {
                id: true,
                username: true,
              },
            },
          },
          orderBy: {
            name: 'asc',
          },
        },
        _count: {
          select: {
            characters: true,
            battleSessions: true,
            documents: true,
          },
        },
      },
    });

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

    // Check if user has access
    const hasAccess =
      campaign.gmId === userId ||
      campaign.members.some((m) => m.userId === userId);

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

    return campaign;
  }

  async update(id: string, dto: UpdateCampaignDto, userId: string) {
    const campaign = await this.findOne(id, userId);

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

    return this.prisma.campaign.update({
      where: { id },
      data: dto,
      include: {
        gm: {
          select: {
            id: true,
            username: true,
            avatarUrl: true,
          },
        },
      },
    });
  }

  async remove(id: string, userId: string, userRole: UserRole) {
    const campaign = await this.findOne(id, userId);

    // Only GM or Admin can delete campaign
    if (campaign.gmId !== userId && userRole !== UserRole.ADMIN) {
      throw new ForbiddenException('Only the GM or an Admin can delete the campaign');
    }

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

    return { message: 'Campaign deleted successfully' };
  }

  async addMember(campaignId: string, dto: AddMemberDto, userId: string) {
    const campaign = await this.findOne(campaignId, userId);

    // Only GM can add members
    if (campaign.gmId !== userId) {
      throw new ForbiddenException('Only the GM can add members');
    }

    // Check if user exists
    const userToAdd = await this.prisma.user.findUnique({
      where: { id: dto.userId },
    });

    if (!userToAdd) {
      throw new NotFoundException('User not found');
    }

    // Check if user is already a member
    const existingMember = await this.prisma.campaignMember.findUnique({
      where: {
        campaignId_userId: {
          campaignId,
          userId: dto.userId,
        },
      },
    });

    if (existingMember) {
      throw new ConflictException('User is already a member of this campaign');
    }

    // Add member
    await this.prisma.campaignMember.create({
      data: {
        campaignId,
        userId: dto.userId,
      },
    });

    return this.findOne(campaignId, userId);
  }

  async removeMember(campaignId: string, memberUserId: string, userId: string) {
    const campaign = await this.findOne(campaignId, userId);

    // Only GM can remove members
    if (campaign.gmId !== userId) {
      throw new ForbiddenException('Only the GM can remove members');
    }

    // Cannot remove GM
    if (memberUserId === campaign.gmId) {
      throw new ForbiddenException('Cannot remove the GM from the campaign');
    }

    // Check if member exists
    const member = await this.prisma.campaignMember.findUnique({
      where: {
        campaignId_userId: {
          campaignId,
          userId: memberUserId,
        },
      },
    });

    if (!member) {
      throw new NotFoundException('Member not found in campaign');
    }

    await this.prisma.campaignMember.delete({
      where: {
        campaignId_userId: {
          campaignId,
          userId: memberUserId,
        },
      },
    });

    return this.findOne(campaignId, userId);
  }
}
