import {
  Injectable, NotFoundException, BadRequestException, ForbiddenException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like, FindOptionsWhere } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { User, Role, WorkSchedule } from './entities/user.entity';
import { EmployeeProfile } from './entities/employee-profile.entity';
import { CareerHistory, CareerEventType } from './entities/career-history.entity';
import { Team } from '../teams/entities/team.entity';
import { Agency } from '../teams/entities/agency.entity';

// ─── DTOs inline (simples, pas besoin de fichiers séparés) ───────────────────

export interface CreateUserDto {
  matricule: string;
  first_name: string;
  last_name: string;
  email: string;
  password?: string;
  role: Role;
  work_schedule?: WorkSchedule;
  hired_at: string;
  team_id?: number;
  agency_id?: number;
  is_active?: boolean;
}

export interface UpdateUserDto {
  first_name?: string;
  last_name?: string;
  email?: string;
  role?: Role;
  work_schedule?: WorkSchedule;
  hired_at?: string;
  team_id?: number;
  agency_id?: number;
  is_active?: boolean;
  password?: string;
}

export interface UpdateProfileDto {
  birth_date?: string;
  address?: string;
  phone?: string;
  emergency_contact_name?: string;
  emergency_contact_phone?: string;
  job_title?: string;
  contract_type?: string;
  skills?: string[];
}

export interface CreateCareerHistoryDto {
  user_id: number;
  event_type: CareerEventType;
  description: string;
  effective_date: string;
}

export interface UsersFilters {
  search?: string;
  role?: string;
  team_id?: number;
  agency_id?: number;
  is_active?: boolean;
  page?: number;
  limit?: number;
}

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly userRepo: Repository<User>,

    @InjectRepository(EmployeeProfile)
    private readonly profileRepo: Repository<EmployeeProfile>,

    @InjectRepository(CareerHistory)
    private readonly careerRepo: Repository<CareerHistory>,

    @InjectRepository(Team)
    private readonly teamRepo: Repository<Team>,

    @InjectRepository(Agency)
    private readonly agencyRepo: Repository<Agency>,
  ) {}

  // ─── Lister les utilisateurs ─────────────────────────────────────────────────
  // Retourne un tableau simple (sans pagination wrapper) pour compatibilité frontend
  async findAll(filters: UsersFilters): Promise<(User & { profile?: EmployeeProfile })[]> {
    const page  = filters.page  ?? 1;
    const limit = filters.limit ?? 200; // haute limite par défaut

    const query = this.userRepo
      .createQueryBuilder('user')
      .leftJoinAndSelect('user.team', 'team')
      .leftJoinAndSelect('user.agency', 'agency')
      .orderBy('user.last_name', 'ASC');

    if (filters.search) {
      query.andWhere(
        '(user.first_name LIKE :s OR user.last_name LIKE :s OR user.email LIKE :s OR user.matricule LIKE :s)',
        { s: `%${filters.search}%` },
      );
    }
    if (filters.role)      query.andWhere('user.role = :role',           { role: filters.role });
    if (filters.team_id)   query.andWhere('user.team_id = :teamId',      { teamId: filters.team_id });
    if (filters.agency_id) query.andWhere('user.agency_id = :agencyId',  { agencyId: filters.agency_id });
    if (filters.is_active !== undefined) {
      query.andWhere('user.is_active = :active', { active: filters.is_active });
    }

    const users = await query
      .skip((page - 1) * limit)
      .take(limit)
      .getMany();

    // Enrichir chaque user avec son profil (pour affichage admin)
    const usersWithProfile = await Promise.all(
      users.map(async (u) => {
        const profile = await this.profileRepo.findOne({ where: { user: { id: u.id } } });
        return { ...u, profile: profile ?? undefined };
      })
    );

    return usersWithProfile;
  }

  // ─── Récupérer un utilisateur avec profil ────────────────────────────────────
  async findOne(id: number): Promise<User & { profile?: EmployeeProfile }> {
    const user = await this.userRepo.findOne({
      where: { id },
      relations: ['team', 'agency'],
    });
    if (!user) throw new NotFoundException(`Utilisateur #${id} introuvable`);

    const profile = await this.profileRepo.findOne({ where: { user: { id } } });
    return { ...user, profile: profile ?? undefined };
  }

  // ─── Créer un utilisateur ────────────────────────────────────────────────────
  async create(dto: CreateUserDto): Promise<User> {
    const existsEmail = await this.userRepo.findOne({ where: { email: dto.email } });
    if (existsEmail) throw new BadRequestException('Email déjà utilisé');

    const existsMatricule = await this.userRepo.findOne({ where: { matricule: dto.matricule } });
    if (existsMatricule) throw new BadRequestException('Matricule déjà utilisé');

    const password_hash = await bcrypt.hash(dto.password ?? 'Password123!', 12);

    const user = this.userRepo.create({
      matricule:     dto.matricule,
      first_name:    dto.first_name,
      last_name:     dto.last_name,
      email:         dto.email,
      password_hash,
      role:          dto.role,
      work_schedule: dto.work_schedule ?? WorkSchedule.FIVE_DAYS,
      hired_at:      new Date(dto.hired_at),
      is_active:     dto.is_active ?? true,
    });

    if (dto.team_id) {
      const team = await this.teamRepo.findOne({ where: { id: dto.team_id } });
      if (team) user.team = team;
    }
    if (dto.agency_id) {
      const agency = await this.agencyRepo.findOne({ where: { id: dto.agency_id } });
      if (agency) user.agency = agency;
    }

    return this.userRepo.save(user);
  }

  // ─── Mettre à jour un utilisateur ───────────────────────────────────────────
  async update(id: number, dto: UpdateUserDto): Promise<User> {
    const user = await this.userRepo.findOne({
      where: { id },
      relations: ['team', 'agency'],
    });
    if (!user) throw new NotFoundException(`Utilisateur #${id} introuvable`);

    if (dto.email && dto.email !== user.email) {
      const exists = await this.userRepo.findOne({ where: { email: dto.email } });
      if (exists) throw new BadRequestException('Email déjà utilisé');
    }

    if (dto.first_name)    user.first_name    = dto.first_name;
    if (dto.last_name)     user.last_name     = dto.last_name;
    if (dto.email)         user.email         = dto.email;
    if (dto.role)          user.role          = dto.role;
    if (dto.work_schedule) user.work_schedule = dto.work_schedule;
    if (dto.hired_at)      user.hired_at      = new Date(dto.hired_at);
    if (dto.is_active !== undefined) user.is_active = dto.is_active;

    if (dto.password) {
      user.password_hash = await bcrypt.hash(dto.password, 12);
    }

    if (dto.team_id !== undefined) {
      const team = dto.team_id
        ? await this.teamRepo.findOne({ where: { id: dto.team_id } })
        : null;
      if (team) user.team = team;
    }
    if (dto.agency_id !== undefined) {
      const agency = dto.agency_id
        ? await this.agencyRepo.findOne({ where: { id: dto.agency_id } })
        : null;
      if (agency) user.agency = agency;
    }

    return this.userRepo.save(user);
  }

  // ─── Activer / désactiver un utilisateur ────────────────────────────────────
  async toggleActive(id: number): Promise<User> {
    const user = await this.userRepo.findOne({ where: { id } });
    if (!user) throw new NotFoundException(`Utilisateur #${id} introuvable`);
    user.is_active = !user.is_active;
    return this.userRepo.save(user);
  }

  // ─── Récupérer le profil d'un utilisateur ───────────────────────────────────
  async getProfile(userId: number): Promise<EmployeeProfile | null> {
    return this.profileRepo.findOne({ where: { user: { id: userId } } });
  }

  // ─── Mettre à jour le profil ─────────────────────────────────────────────────
  async updateProfile(userId: number, dto: UpdateProfileDto): Promise<EmployeeProfile> {
    let profile = await this.profileRepo.findOne({ where: { user: { id: userId } } });

    if (!profile) {
      const user = await this.userRepo.findOne({ where: { id: userId } });
      if (!user) throw new NotFoundException(`Utilisateur #${userId} introuvable`);

      profile = this.profileRepo.create({
        user,
        job_title: dto.job_title ?? 'Non renseigné',
      });
    }

    if (dto.birth_date)               profile.birth_date               = new Date(dto.birth_date);
    if (dto.address !== undefined)    profile.address                  = dto.address;
    if (dto.phone !== undefined)      profile.phone                    = dto.phone;
    if (dto.emergency_contact_name !== undefined) profile.emergency_contact_name  = dto.emergency_contact_name;
    if (dto.emergency_contact_phone !== undefined) profile.emergency_contact_phone = dto.emergency_contact_phone;
    if (dto.job_title)                profile.job_title                = dto.job_title;
    if (dto.contract_type !== undefined) profile.contract_type         = dto.contract_type;
    if (dto.skills !== undefined)     profile.skills                   = dto.skills;

    return this.profileRepo.save(profile);
  }

  // ─── Historique de carrière ───────────────────────────────────────────────────
  async getCareerHistory(userId: number): Promise<CareerHistory[]> {
    return this.careerRepo.find({
      where: { user: { id: userId } },
      relations: ['created_by'],
      order: { effective_date: 'DESC' },
    });
  }

  async addCareerEvent(dto: CreateCareerHistoryDto, createdBy: User): Promise<CareerHistory> {
    const user = await this.userRepo.findOne({ where: { id: dto.user_id } });
    if (!user) throw new NotFoundException(`Utilisateur #${dto.user_id} introuvable`);

    const event = this.careerRepo.create({
      user,
      event_type:     dto.event_type,
      description:    dto.description,
      effective_date: new Date(dto.effective_date),
      created_by:     createdBy,
    });

    return this.careerRepo.save(event);
  }

  // ─── Upload avatar ────────────────────────────────────────────────────────────
  async updateAvatar(userId: number, filename: string): Promise<User> {
    const user = await this.userRepo.findOne({ where: { id: userId } });
    if (!user) throw new NotFoundException(`Utilisateur #${userId} introuvable`);
    user.avatar_url = `/uploads/${filename}`;
    return this.userRepo.save(user);
  }

  // ─── Stats RH ─────────────────────────────────────────────────────────────────
  async getStats(): Promise<{
    total: number;
    by_role: { role: string; count: number }[];
    by_team: { team_name: string; count: number }[];
    by_schedule: { schedule: string; count: number }[];
  }> {
    const total = await this.userRepo.count({ where: { is_active: true } });

    const by_role = await this.userRepo
      .createQueryBuilder('u')
      .select('u.role', 'role')
      .addSelect('COUNT(*)', 'count')
      .where('u.is_active = true')
      .groupBy('u.role')
      .getRawMany();

    const by_team = await this.userRepo
      .createQueryBuilder('u')
      .leftJoin('u.team', 't')
      .select('t.name', 'team_name')
      .addSelect('COUNT(*)', 'count')
      .where('u.is_active = true')
      .groupBy('t.name')
      .getRawMany();

    const by_schedule = await this.userRepo
      .createQueryBuilder('u')
      .select('u.work_schedule', 'schedule')
      .addSelect('COUNT(*)', 'count')
      .where('u.is_active = true')
      .groupBy('u.work_schedule')
      .getRawMany();

    return { total, by_role, by_team, by_schedule };
  }
}