import {
  Injectable,
  NotFoundException,
  ConflictException,
  BadRequestException,
} from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import { CreateSkillDto, UpdateSkillDto, QuerySkillsDto } from './dto';
import { Prisma } from '@prisma/client';

/**
 * Service for managing skills in the skill matrix
 */
@Injectable()
export class SkillsService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Create a new skill
   */
  async create(createDto: CreateSkillDto, userId: string) {
    // Check for duplicate skill name in the same department
    const existingSkill = await this.prisma.skill.findFirst({
      where: {
        name: createDto.name,
        departmentId: createDto.departmentId ?? null,
      },
    });

    if (existingSkill) {
      throw new ConflictException(
        `Skill "${createDto.name}" already exists in this scope`,
      );
    }

    // Validate department exists if provided
    if (createDto.departmentId) {
      const department = await this.prisma.department.findUnique({
        where: { id: createDto.departmentId },
      });

      if (!department) {
        throw new NotFoundException(
          `Department with ID ${createDto.departmentId} not found`,
        );
      }
    }

    return this.prisma.skill.create({
      data: {
        name: createDto.name,
        description: createDto.description,
        category: createDto.category,
        departmentId: createDto.departmentId,
        isActive: createDto.isActive ?? true,
      },
      include: {
        department: {
          select: {
            id: true,
            name: true,
            code: true,
          },
        },
        _count: {
          select: {
            entries: true,
          },
        },
      },
    });
  }

  /**
   * Find all skills with filtering and pagination
   */
  async findAll(query: QuerySkillsDto) {
    const {
      page = 1,
      limit = 20,
      search,
      departmentId,
      category,
      isActive,
      includeGlobal = true,
      sortBy,
      sortOrder,
    } = query;

    const skip = (page - 1) * limit;

    // Build where clause
    const whereConditions: Prisma.SkillWhereInput[] = [];

    // Search filter
    if (search) {
      whereConditions.push({
        OR: [
          { name: { contains: search, mode: 'insensitive' } },
          { description: { contains: search, mode: 'insensitive' } },
        ],
      });
    }

    // Department filter
    if (departmentId) {
      if (includeGlobal) {
        whereConditions.push({
          OR: [{ departmentId: departmentId }, { departmentId: null }],
        });
      } else {
        whereConditions.push({ departmentId: departmentId });
      }
    }

    // Category filter
    if (category) {
      whereConditions.push({ category: category });
    }

    // Active filter
    if (isActive !== undefined) {
      whereConditions.push({ isActive: isActive });
    }

    const where: Prisma.SkillWhereInput =
      whereConditions.length > 0 ? { AND: whereConditions } : {};

    // Build orderBy
    const orderBy: Prisma.SkillOrderByWithRelationInput = sortBy
      ? { [sortBy]: sortOrder || 'asc' }
      : { name: 'asc' };

    const [skills, total] = await Promise.all([
      this.prisma.skill.findMany({
        where,
        skip,
        take: limit,
        orderBy,
        include: {
          department: {
            select: {
              id: true,
              name: true,
              code: true,
            },
          },
          _count: {
            select: {
              entries: true,
            },
          },
        },
      }),
      this.prisma.skill.count({ where }),
    ]);

    return {
      data: skills,
      meta: {
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      },
    };
  }

  /**
   * Find a single skill by ID
   */
  async findOne(id: string) {
    const skill = await this.prisma.skill.findUnique({
      where: { id },
      include: {
        department: {
          select: {
            id: true,
            name: true,
            code: true,
          },
        },
        _count: {
          select: {
            entries: true,
          },
        },
      },
    });

    if (!skill) {
      throw new NotFoundException(`Skill with ID ${id} not found`);
    }

    return skill;
  }

  /**
   * Update a skill
   */
  async update(id: string, updateDto: UpdateSkillDto) {
    await this.findOne(id);

    // Check for duplicate name if updating
    if (updateDto.name) {
      const currentSkill = await this.prisma.skill.findUnique({
        where: { id },
      });

      const existingSkill = await this.prisma.skill.findFirst({
        where: {
          name: updateDto.name,
          departmentId: updateDto.departmentId ?? currentSkill?.departmentId ?? null,
          id: { not: id },
        },
      });

      if (existingSkill) {
        throw new ConflictException(
          `Skill "${updateDto.name}" already exists in this scope`,
        );
      }
    }

    // Validate department if updating
    if (updateDto.departmentId) {
      const department = await this.prisma.department.findUnique({
        where: { id: updateDto.departmentId },
      });

      if (!department) {
        throw new NotFoundException(
          `Department with ID ${updateDto.departmentId} not found`,
        );
      }
    }

    return this.prisma.skill.update({
      where: { id },
      data: updateDto,
      include: {
        department: {
          select: {
            id: true,
            name: true,
            code: true,
          },
        },
        _count: {
          select: {
            entries: true,
          },
        },
      },
    });
  }

  /**
   * Soft delete a skill (deactivate)
   */
  async remove(id: string) {
    await this.findOne(id);

    return this.prisma.skill.update({
      where: { id },
      data: { isActive: false },
      include: {
        department: {
          select: {
            id: true,
            name: true,
            code: true,
          },
        },
        _count: {
          select: {
            entries: true,
          },
        },
      },
    });
  }

  /**
   * Get all categories
   */
  async getCategories() {
    const skills = await this.prisma.skill.findMany({
      where: { isActive: true },
      select: { category: true },
      distinct: ['category'],
    });

    return skills
      .map((s) => s.category)
      .filter((c): c is string => c !== null);
  }

  /**
   * Get skills for a department including global skills
   */
  async getSkillsForDepartment(departmentId: string) {
    // Verify department exists
    const department = await this.prisma.department.findUnique({
      where: { id: departmentId },
    });

    if (!department) {
      throw new NotFoundException(`Department with ID ${departmentId} not found`);
    }

    return this.prisma.skill.findMany({
      where: {
        isActive: true,
        OR: [{ departmentId: departmentId }, { departmentId: null }],
      },
      orderBy: [{ category: 'asc' }, { name: 'asc' }],
      include: {
        department: {
          select: {
            id: true,
            name: true,
            code: true,
          },
        },
      },
    });
  }
}
