import {
  BadRequestException,
  ConflictException,
  ForbiddenException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Children, StatutEnfantType } from 'src/entities/children.entity';
import { Parents } from 'src/entities/parents.entity';
import { ParentsChildren } from 'src/entities/parents_children.entity';
import { RoleType, Users } from 'src/entities/users.entity';
import { CreateEnfantsDto } from './dto/create_enfants.dto';

const STAFF_ROLES: RoleType[] = [
  RoleType.GESTIONNAIRE,
  RoleType.ADMINISTRATEUR,
  RoleType.SUPER_ADMIN,
];

@Injectable()
export class EnfantsService {
  constructor(
    @InjectRepository(Children)
    private readonly childrenRepository: Repository<Children>,
    @InjectRepository(Parents)
    private readonly parentsRepository: Repository<Parents>,
    @InjectRepository(ParentsChildren)
    private readonly parentsChildrenRepository: Repository<ParentsChildren>,
  ) { }

  private isStaff(user: Users): boolean {
    return STAFF_ROLES.includes(user.role);
  }

  /**
   * Création d'un enfant.
   * - PARENT : rattache au parent connecté (multipart photo optionnel).
   * - Staff : `parent_user_id` obligatoire ; JSON sans photo OK ;
   *   avec photo → multipart (même stockage `/uploads/photos/...`). Ticket #132.
   */
  async create(
    dto: CreateEnfantsDto,
    currentUser: Users,
    photoFile?: Express.Multer.File,
  ): Promise<Children> {
    const pivotUserId = this.resolvePivotParentUserId(dto, currentUser);

    const parent = await this.parentsRepository.findOne({
      where: { user_id: pivotUserId },
      relations: ['co_parent'],
    });
    if (!parent) throw new NotFoundException('Parent introuvable');

    // Vérif métier simple (aligné comportement historique parent)
    if (dto.status !== StatutEnfantType.A_NAITRE && !dto.birth_date) {
      throw new BadRequestException('Un enfant né doit avoir une date de naissance');
    }

    // Vérif doublon éventuel (ex: même prénom + date de naissance)
    const exist = await this.childrenRepository.findOne({
      where: {
        first_name: dto.first_name,
        last_name: dto.last_name,
        birth_date: dto.birth_date ? new Date(dto.birth_date) : undefined,
      },
    });
    if (exist) throw new ConflictException('Cet enfant existe déjà');

    // Gestion de la photo uploadée (multipart parent ou staff)
    let photoUrl = dto.photo_url;
    let consentAt: Date | undefined;
    if (photoFile) {
      photoUrl = `/uploads/photos/${photoFile.filename}`;
      if (dto.consent_photo) {
        consentAt = new Date();
      }
    } else if (dto.consent_photo) {
      consentAt = dto.consent_photo_at
        ? new Date(dto.consent_photo_at)
        : new Date();
    }

    const child = this.childrenRepository.create({
      status: dto.status,
      first_name: dto.first_name,
      last_name: dto.last_name,
      gender: dto.gender,
      birth_date: dto.birth_date ? new Date(dto.birth_date) : undefined,
      due_date: dto.due_date ? new Date(dto.due_date) : undefined,
      photo_url: photoUrl,
      consent_photo: !!dto.consent_photo,
      consent_photo_at: consentAt,
      is_multiple: !!dto.is_multiple,
    });
    await this.childrenRepository.save(child);

    // Lien parent-enfant (pivot)
    await this.parentsChildrenRepository.save(
      this.parentsChildrenRepository.create({
        parentId: parent.user_id,
        enfantId: child.id,
      }),
    );

    // Rattachement automatique au co-parent s'il existe
    if (parent.co_parent) {
      await this.parentsChildrenRepository.save(
        this.parentsChildrenRepository.create({
          parentId: parent.co_parent.id,
          enfantId: child.id,
        }),
      );
    }

    return this.findOne(child.id, currentUser);
  }

  private resolvePivotParentUserId(
    dto: CreateEnfantsDto,
    currentUser: Users,
  ): string {
    if (this.isStaff(currentUser)) {
      const id = dto.parent_user_id?.trim();
      if (!id) {
        throw new BadRequestException(
          'parent_user_id est obligatoire pour créer un enfant (staff)',
        );
      }
      return id;
    }

    if (currentUser.role === RoleType.PARENT) {
      if (
        dto.parent_user_id &&
        dto.parent_user_id.trim() !== currentUser.id
      ) {
        throw new ForbiddenException(
          'Un parent ne peut pas créer un enfant pour un autre compte',
        );
      }
      return currentUser.id;
    }

    throw new ForbiddenException('Accès interdit');
  }

  /** Flag API #157 — true si aucun lien enfants_parents. */
  private withSansResponsable(child: Children): Children & { sans_responsable: boolean } {
    return Object.assign(child, {
      sans_responsable: !child.parentLinks || child.parentLinks.length === 0,
    });
  }

  // Liste des enfants (admin/gestionnaire) — inclut les orphelins (parentLinks: [])
  async findAll(): Promise<Array<Children & { sans_responsable: boolean }>> {
    const children = await this.childrenRepository.find({
      relations: ['parentLinks', 'parentLinks.parent', 'parentLinks.parent.user'],
      order: { last_name: 'ASC', first_name: 'ASC' },
    });
    return children.map((c) => this.withSansResponsable(c));
  }

  // Récupérer un enfant par id
  async findOne(
    id: string,
    currentUser: Users,
  ): Promise<Children & { sans_responsable: boolean }> {
    const child = await this.childrenRepository.findOne({
      where: { id },
      relations: ['parentLinks', 'parentLinks.parent', 'parentLinks.parent.user'],
    });
    if (!child) throw new NotFoundException('Enfant introuvable');

    switch (currentUser.role) {
      case RoleType.PARENT:
        if (!child.parentLinks.some(link => link.parentId === currentUser.id)) {
          throw new ForbiddenException('Cet enfant ne vous appartient pas');
        }
        break;

      case RoleType.ADMINISTRATEUR:
      case RoleType.SUPER_ADMIN:
      case RoleType.GESTIONNAIRE:
        // accès complet (y compris orphelins)
        break;

      default:
        throw new ForbiddenException('Accès interdit');
    }

    return this.withSansResponsable(child);
  }


  // Mise à jour
  async update(id: string, dto: Partial<CreateEnfantsDto>, currentUser: Users): Promise<Children> {
    const child = await this.childrenRepository.findOne({ where: { id } });
    if (!child) throw new NotFoundException('Enfant introuvable');

    const { parent_user_id: _ignored, ...rest } = dto;
    const patch: Partial<Children> = { ...rest } as Partial<Children>;
    if (dto.consent_photo !== undefined) {
      patch.consent_photo = dto.consent_photo;
      patch.consent_photo_at = dto.consent_photo ? new Date() : null!;
    }

    await this.childrenRepository.update(id, patch);
    return this.findOne(id, currentUser);
  }

  // Suppression
  async remove(id: string): Promise<void> {
    await this.childrenRepository.delete(id);
  }
}
