import {
    ConflictException,
    Injectable,
    NotFoundException,
  } from '@nestjs/common';
  import * as argon2 from 'argon2';
  import { TenantConnectionService } from '../../database/tenant-connection.service';
  import { requireCurrentTenant } from '../../database/tenant-context';
  import { User } from '../../database/tenant/entities/user.entity';
  import { UserRole } from '../../database/tenant/entities/user-role.entity';
  import { CreateUserDto, UpdateUserDto } from './dto/create-user.dto';

  @Injectable()
  export class UsersService {
    constructor(private readonly tenantConn: TenantConnectionService) {}

    private async ds() {
      const ctx = requireCurrentTenant();
      return this.tenantConn.getTenantDataSource(ctx.subdomain);
    }

    async list() {
      const ds = await this.ds();
      return ds.getRepository(User).find({
        relations: { office: true, userRoles: { role: true } },
        order: { createdAt: 'DESC' },
      });
    }

    async getOne(id: string) {
      const ds = await this.ds();
      const u = await ds.getRepository(User).findOne({
        where: { id },
        relations: { office: true, userRoles: { role: true } },
      });
      if (!u) throw new NotFoundException('User not found');
      return u;
    }

    async create(dto: CreateUserDto) {
      const ds = await this.ds();
      const repo = ds.getRepository(User);
      const email = dto.email.toLowerCase();
      const existing = await repo.findOne({ where: { email } });
      if (existing) throw new ConflictException('Email already in use');

      const passwordHash = await argon2.hash(dto.password);
      const user = await repo.save(
        repo.create({
          email,
          username: dto.username,
          fullName: dto.fullName,
          phone: dto.phone,
          officeId: dto.officeId,
          passwordHash,
          active: true,
          isBypass: dto.isBypass ?? false,
        }),
      );

      if (dto.roleIds && dto.roleIds.length > 0) {
        const urRepo = ds.getRepository(UserRole);
        await urRepo.save(
          dto.roleIds.map((roleId) => urRepo.create({ userId: user.id, roleId })),
        );
      }

      return this.getOne(user.id);
    }

    async update(id: string, dto: UpdateUserDto) {
      const ds = await this.ds();
      const repo = ds.getRepository(User);
      const user = await repo.findOne({ where: { id } });
      if (!user) throw new NotFoundException('User not found');

      if (dto.fullName !== undefined) user.fullName = dto.fullName;
      if (dto.phone !== undefined) user.phone = dto.phone;
      if (dto.active !== undefined) user.active = dto.active;
      if (dto.officeId !== undefined) user.officeId = dto.officeId;
      await repo.save(user);

      if (dto.roleIds) {
        const urRepo = ds.getRepository(UserRole);
        await urRepo.delete({ userId: id });
        if (dto.roleIds.length > 0) {
          await urRepo.save(
            dto.roleIds.map((roleId) => urRepo.create({ userId: id, roleId })),
          );
        }
      }
      return this.getOne(id);
    }

    async remove(id: string) {
      const ds = await this.ds();
      await ds.getRepository(User).delete(id);
      return { ok: true };
    }
  }
  