import {
    Injectable,
    UnauthorizedException,
    Inject,
    Logger,
  } from '@nestjs/common';
  import { JwtService } from '@nestjs/jwt';
  import { DataSource } from 'typeorm';
  import * as argon2 from 'argon2';
  import { TenantConnectionService } from '../../database/tenant-connection.service';
  import { LANDLORD_DATA_SOURCE } from '../../database/database.module';
  import { Tenant } from '../../database/landlord/entities/tenant.entity';
  import { User } from '../../database/tenant/entities/user.entity';
  import { UserRole } from '../../database/tenant/entities/user-role.entity';
  import { RolePermission } from '../../database/tenant/entities/role-permission.entity';
  import { Permission } from '../../database/tenant/entities/permission.entity';

  export interface LoginResult {
    accessToken: string;
    user: {
      id: string;
      email: string;
      fullName: string;
      permissions: string[];
      isBypass: boolean;
    };
    tenant: { id: string; subdomain: string; name: string };
  }

  @Injectable()
  export class AuthService {
    private readonly logger = new Logger(AuthService.name);

    constructor(
      private readonly jwt: JwtService,
      private readonly tenantConn: TenantConnectionService,
      @Inject(LANDLORD_DATA_SOURCE) private readonly landlord: DataSource,
    ) {}

    async login(
      subdomain: string,
      email: string,
      password: string,
    ): Promise<LoginResult> {
      const tenant = await this.landlord
        .getRepository(Tenant)
        .findOne({ where: { subdomain } });
      if (!tenant) throw new UnauthorizedException('Invalid tenant');
      if (tenant.status !== 'active') {
        throw new UnauthorizedException('Tenant is not active');
      }

      const ds = await this.tenantConn.getTenantDataSource(subdomain);
      const userRepo = ds.getRepository(User);
      const user = await userRepo.findOne({
        where: { email: email.toLowerCase() },
      });
      if (!user || !user.active) {
        throw new UnauthorizedException('Invalid credentials');
      }

      const ok = await argon2.verify(user.passwordHash, password);
      if (!ok) throw new UnauthorizedException('Invalid credentials');

      const permissions = await this.collectPermissions(ds, user.id, user.isBypass);

      user.lastLoginAt = new Date();
      await userRepo.save(user);

      const payload = {
        sub: user.id,
        email: user.email,
        tenantId: tenant.id,
        subdomain: tenant.subdomain,
        permissions,
      };
      const accessToken = await this.jwt.signAsync(payload);

      return {
        accessToken,
        user: {
          id: user.id,
          email: user.email,
          fullName: user.fullName,
          permissions,
          isBypass: user.isBypass,
        },
        tenant: { id: tenant.id, subdomain: tenant.subdomain, name: tenant.name },
      };
    }

    private async collectPermissions(
      ds: DataSource,
      userId: string,
      isBypass: boolean,
    ): Promise<string[]> {
      if (isBypass) return ['*'];

      const userRoles = await ds
        .getRepository(UserRole)
        .find({ where: { userId } });
      if (userRoles.length === 0) return [];

      const roleIds = userRoles.map((ur) => ur.roleId);
      const rolePerms = await ds
        .getRepository(RolePermission)
        .createQueryBuilder('rp')
        .where('rp.role_id IN (:...roleIds)', { roleIds })
        .getMany();
      if (rolePerms.length === 0) return [];

      const permIds = [...new Set(rolePerms.map((rp) => rp.permissionId))];
      const perms = await ds
        .getRepository(Permission)
        .createQueryBuilder('p')
        .where('p.id IN (:...permIds)', { permIds })
        .getMany();
      return [...new Set(perms.map((p) => p.key))];
    }
  }
  