import bcrypt from 'bcryptjs';
import { Repository } from 'typeorm';
import { AppDataSource } from '../../config/typeorm.js';
import { User, UserStatus, Role } from './entities/index.js';
import { tokenService, TokenPair, RequestMetadata } from './services/token.service.js';
import { UnauthorizedError, ValidationError, NotFoundError } from '../../shared/types/index.js';
import { logger } from '../../shared/utils/logger.js';

export interface LoginDto {
  email: string;
  password: string;
  metadata?: RequestMetadata; // IP and user agent for session tracking
}

export interface RegisterDto {
  email: string;
  password: string;
  // Soporta ambos formatos para compatibilidad frontend/backend
  full_name?: string;
  firstName?: string;
  lastName?: string;
  tenant_id?: string;
  companyName?: string;
}

/**
 * Transforma full_name a firstName/lastName para respuesta al frontend
 */
export function splitFullName(fullName: string): { firstName: string; lastName: string } {
  const parts = fullName.trim().split(/\s+/);
  if (parts.length === 1) {
    return { firstName: parts[0], lastName: '' };
  }
  const firstName = parts[0];
  const lastName = parts.slice(1).join(' ');
  return { firstName, lastName };
}

/**
 * Transforma firstName/lastName a full_name para almacenar en BD
 */
export function buildFullName(firstName?: string, lastName?: string, fullName?: string): string {
  if (fullName) return fullName.trim();
  return `${firstName || ''} ${lastName || ''}`.trim();
}

export interface LoginResponse {
  user: Omit<User, 'passwordHash'> & { firstName: string; lastName: string };
  tokens: TokenPair;
}

class AuthService {
  private userRepository: Repository<User>;

  constructor() {
    this.userRepository = AppDataSource.getRepository(User);
  }

  async login(dto: LoginDto): Promise<LoginResponse> {
    // Find user by email using TypeORM
    const user = await this.userRepository.findOne({
      where: { email: dto.email.toLowerCase(), status: UserStatus.ACTIVE },
      relations: ['roles'],
    });

    if (!user) {
      throw new UnauthorizedError('Credenciales inválidas');
    }

    // Verify password
    const isValidPassword = await bcrypt.compare(dto.password, user.passwordHash || '');
    if (!isValidPassword) {
      throw new UnauthorizedError('Credenciales inválidas');
    }

    // Update last login
    user.lastLoginAt = new Date();
    user.loginCount += 1;
    if (dto.metadata?.ipAddress) {
      user.lastLoginIp = dto.metadata.ipAddress;
    }
    await this.userRepository.save(user);

    // Generate token pair using TokenService
    const metadata: RequestMetadata = dto.metadata || {
      ipAddress: 'unknown',
      userAgent: 'unknown',
    };
    const tokens = await tokenService.generateTokenPair(user, metadata);

    // Transform fullName to firstName/lastName for frontend response
    const { firstName, lastName } = splitFullName(user.fullName);

    // Remove passwordHash from response and add firstName/lastName
    const { passwordHash, ...userWithoutPassword } = user;
    const userResponse = {
      ...userWithoutPassword,
      firstName,
      lastName,
    };

    logger.info('User logged in', { userId: user.id, email: user.email });

    return {
      user: userResponse as any,
      tokens,
    };
  }

  async register(dto: RegisterDto): Promise<LoginResponse> {
    // Check if email already exists using TypeORM
    const existingUser = await this.userRepository.findOne({
      where: { email: dto.email.toLowerCase() },
    });

    if (existingUser) {
      throw new ValidationError('El email ya está registrado');
    }

    // Transform firstName/lastName to fullName for database storage
    const fullName = buildFullName(dto.firstName, dto.lastName, dto.full_name);

    // Hash password
    const passwordHash = await bcrypt.hash(dto.password, 10);

    // Generate tenantId if not provided (new company registration)
    const tenantId = dto.tenant_id || crypto.randomUUID();

    // Create user using TypeORM
    const newUser = this.userRepository.create({
      email: dto.email.toLowerCase(),
      passwordHash,
      fullName,
      tenantId,
      status: UserStatus.ACTIVE,
    });

    await this.userRepository.save(newUser);

    // Load roles relation for token generation
    const userWithRoles = await this.userRepository.findOne({
      where: { id: newUser.id },
      relations: ['roles'],
    });

    if (!userWithRoles) {
      throw new Error('Error al crear usuario');
    }

    // Generate token pair using TokenService
    const metadata: RequestMetadata = {
      ipAddress: 'unknown',
      userAgent: 'unknown',
    };
    const tokens = await tokenService.generateTokenPair(userWithRoles, metadata);

    // Transform fullName to firstName/lastName for frontend response
    const { firstName, lastName } = splitFullName(userWithRoles.fullName);

    // Remove passwordHash from response and add firstName/lastName
    const { passwordHash: _, ...userWithoutPassword } = userWithRoles;
    const userResponse = {
      ...userWithoutPassword,
      firstName,
      lastName,
    };

    logger.info('User registered', { userId: userWithRoles.id, email: userWithRoles.email });

    return {
      user: userResponse as any,
      tokens,
    };
  }

  async refreshToken(refreshToken: string, metadata: RequestMetadata): Promise<TokenPair> {
    // Delegate completely to TokenService
    return tokenService.refreshTokens(refreshToken, metadata);
  }

  async logout(sessionId: string): Promise<void> {
    await tokenService.revokeSession(sessionId, 'user_logout');
  }

  async logoutAll(userId: string): Promise<number> {
    return tokenService.revokeAllUserSessions(userId, 'logout_all');
  }

  async changePassword(userId: string, currentPassword: string, newPassword: string): Promise<void> {
    // Find user using TypeORM
    const user = await this.userRepository.findOne({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundError('Usuario no encontrado');
    }

    // Verify current password
    const isValidPassword = await bcrypt.compare(currentPassword, user.passwordHash || '');
    if (!isValidPassword) {
      throw new UnauthorizedError('Contraseña actual incorrecta');
    }

    // Hash new password and update user
    const newPasswordHash = await bcrypt.hash(newPassword, 10);
    user.passwordHash = newPasswordHash;
    user.updatedAt = new Date();
    await this.userRepository.save(user);

    // Revoke all sessions after password change for security
    const revokedCount = await tokenService.revokeAllUserSessions(userId, 'password_changed');

    logger.info('Password changed and all sessions revoked', { userId, revokedCount });
  }

  async getProfile(userId: string): Promise<Omit<User, 'passwordHash'>> {
    // Find user using TypeORM with relations
    const user = await this.userRepository.findOne({
      where: { id: userId },
      relations: ['roles', 'companies'],
    });

    if (!user) {
      throw new NotFoundError('Usuario no encontrado');
    }

    // Remove passwordHash from response
    const { passwordHash, ...userWithoutPassword } = user;
    return userWithoutPassword;
  }
}

export const authService = new AuthService();
