import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from "@nestjs/common";
import { Repository } from "typeorm";
import { UserRole } from "../auth/entities/usuario.entity";
import { ocultarCredencialesTienda } from "../auth/ocultar-credenciales-tienda";
import { TenantRepositoryAccessor } from "../tenancy/tenant-repository.accessor";
import { CreateMecanicoDto } from "./dto/create-mecanico.dto";
import { UpdateMecanicoDto } from "./dto/update-mecanico.dto";
import { Mecanico } from "./entities/mecanico.entity";

@Injectable()
export class MecanicosService {
  constructor(private readonly tenantAccessor: TenantRepositoryAccessor) {}

  private get mecanicoRepository(): Repository<Mecanico> {
    return this.tenantAccessor.repositoryFor(Mecanico);
  }

  async findAll(filters: { activo?: boolean }, user?: any) {
    const query = this.mecanicoRepository
      .createQueryBuilder("mecanico")
      .leftJoinAndSelect("mecanico.tienda", "tienda")
      .orderBy("mecanico.nombre", "ASC");

    if (filters.activo !== undefined) {
      query.andWhere("mecanico.activo = :activo", {
        activo: filters.activo,
      });
    }

    this.applyStoreScope(query, user);

    const rows = await query.getMany();
    return rows.map(ocultarCredencialesTienda);
  }

  async findById(id: number, user?: any) {
    const mecanico = await this.mecanicoRepository.findOne({
      where: { id },
      relations: ["tienda"],
    });

    if (!mecanico) {
      throw new NotFoundException(`Mecánico ${id} no encontrado`);
    }

    this.ensureStoreAccess(mecanico, user);
    return ocultarCredencialesTienda(mecanico);
  }

  async create(dto: CreateMecanicoDto, user?: any) {
    const nombre = String(dto.nombre || "").trim();
    if (!nombre) {
      throw new BadRequestException("El nombre del mecánico es requerido");
    }

    const mecanico = this.mecanicoRepository.create({
      nombre,
      telefono: dto.telefono?.trim() || null,
      email: dto.email?.trim() || null,
      especialidad: dto.especialidad?.trim() || null,
      tiendaId: this.resolveTiendaId(dto.tiendaId, user),
      activo: true,
    });

    return this.mecanicoRepository.save(mecanico);
  }

  async update(id: number, dto: UpdateMecanicoDto, user?: any) {
    const mecanico = await this.findById(id, user);

    if (dto.nombre !== undefined) {
      const nombre = String(dto.nombre || "").trim();
      if (!nombre) {
        throw new BadRequestException("El nombre del mecánico es requerido");
      }
      mecanico.nombre = nombre;
    }

    if (dto.telefono !== undefined) {
      mecanico.telefono = dto.telefono?.trim() || null;
    }
    if (dto.email !== undefined) {
      mecanico.email = dto.email?.trim() || null;
    }
    if (dto.especialidad !== undefined) {
      mecanico.especialidad = dto.especialidad?.trim() || null;
    }
    if (dto.tiendaId !== undefined) {
      mecanico.tiendaId = this.resolveTiendaId(dto.tiendaId, user);
    }

    return this.mecanicoRepository.save(mecanico);
  }

  async setActive(id: number, activo: boolean, user?: any) {
    const mecanico = await this.findById(id, user);
    mecanico.activo = activo;
    await this.mecanicoRepository.save(mecanico);
    return { ok: true };
  }

  async ensureAssignable(id: number, user?: any) {
    const mecanico = await this.findById(id, user);
    if (!mecanico.activo) {
      throw new BadRequestException("El mecánico seleccionado está dado de baja");
    }
    return mecanico;
  }

  private isAdmin(user?: any) {
    return user?.rol === UserRole.SUPER_ADMIN || user?.rol === UserRole.ADMIN;
  }

  private applyStoreScope(queryBuilder: any, user?: any) {
    if (!user || this.isAdmin(user)) {
      return;
    }

    if (user.tiendaId) {
      queryBuilder.andWhere(
        "(mecanico.tiendaId IS NULL OR mecanico.tiendaId = :tiendaId)",
        { tiendaId: user.tiendaId },
      );
      return;
    }

    queryBuilder.andWhere("1 = 0");
  }

  private ensureStoreAccess(mecanico: Mecanico, user?: any) {
    if (!user || this.isAdmin(user)) {
      return;
    }

    if (mecanico.tiendaId && Number(mecanico.tiendaId) !== Number(user.tiendaId)) {
      throw new NotFoundException(`Mecánico ${mecanico.id} no encontrado`);
    }
  }

  private resolveTiendaId(dtoTiendaId?: number, user?: any) {
    if (this.isAdmin(user)) {
      return dtoTiendaId ?? user?.tiendaId ?? null;
    }

    return user?.tiendaId ?? null;
  }
}
