import bcrypt from 'bcryptjs';
import { query, queryOne } from '../../config/database.js';
import { User, PaginationParams, NotFoundError, ValidationError } from '../../shared/types/index.js';
import { logger } from '../../shared/utils/logger.js';
import { splitFullName, buildFullName } from '../auth/auth.service.js';

export interface CreateUserDto {
  tenant_id: string;
  email: string;
  password: string;
  // Soporta ambos formatos para compatibilidad frontend/backend
  full_name?: string;
  firstName?: string;
  lastName?: string;
  status?: 'active' | 'inactive' | 'pending';
  is_superuser?: boolean;
}

export interface UpdateUserDto {
  email?: string;
  full_name?: string;
  firstName?: string;
  lastName?: string;
  status?: 'active' | 'inactive' | 'pending' | 'suspended';
}

/**
 * Transforma usuario de BD a formato frontend (con firstName/lastName)
 */
function transformUserResponse(user: User): Omit<User, 'password_hash'> & { firstName: string; lastName: string } {
  const { password_hash, full_name, ...rest } = user as User & { password_hash?: string };
  const { firstName, lastName } = splitFullName(full_name || '');
  return { ...rest, firstName, lastName } as unknown as Omit<User, 'password_hash'> & { firstName: string; lastName: string };
}

export interface UsersListResult {
  users: Omit<User, 'password_hash'>[];
  total: number;
}

class UsersService {
  async findAll(tenantId: string, params: PaginationParams): Promise<UsersListResult> {
    const { page, limit, sortBy = 'created_at', sortOrder = 'desc' } = params;
    const offset = (page - 1) * limit;

    // Validate sort column to prevent SQL injection
    const allowedSortColumns = ['created_at', 'email', 'full_name', 'status'];
    const safeSort = allowedSortColumns.includes(sortBy) ? sortBy : 'created_at';
    const safeOrder = sortOrder === 'asc' ? 'ASC' : 'DESC';

    const users = await query<User>(
      `SELECT id, tenant_id, email, full_name, status, is_superuser,
              email_verified_at, last_login_at, created_at, updated_at
       FROM auth.users
       WHERE tenant_id = $1
       ORDER BY ${safeSort} ${safeOrder}
       LIMIT $2 OFFSET $3`,
      [tenantId, limit, offset]
    );

    const countResult = await queryOne<{ count: string }>(
      'SELECT COUNT(*) as count FROM auth.users WHERE tenant_id = $1',
      [tenantId]
    );

    return {
      users: users.map(transformUserResponse),
      total: parseInt(countResult?.count || '0', 10),
    };
  }

  async findById(tenantId: string, userId: string): Promise<Omit<User, 'password_hash'>> {
    const user = await queryOne<User>(
      `SELECT id, tenant_id, email, full_name, status, is_superuser,
              email_verified_at, last_login_at, created_at, updated_at
       FROM auth.users
       WHERE id = $1 AND tenant_id = $2`,
      [userId, tenantId]
    );

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

    // Transformar a formato frontend con firstName/lastName
    return transformUserResponse(user);
  }

  async create(dto: CreateUserDto): Promise<Omit<User, 'password_hash'>> {
    // Check if email already exists
    const existingUser = await queryOne<User>(
      'SELECT id FROM auth.users WHERE email = $1',
      [dto.email.toLowerCase()]
    );

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

    // Transformar firstName/lastName a full_name para almacenar en BD
    const fullName = buildFullName(dto.firstName, dto.lastName, dto.full_name);

    const password_hash = await bcrypt.hash(dto.password, 10);

    const user = await queryOne<User>(
      `INSERT INTO auth.users (tenant_id, email, password_hash, full_name, status, is_superuser, created_at)
       VALUES ($1, $2, $3, $4, $5, $6, NOW())
       RETURNING id, tenant_id, email, full_name, status, is_superuser, created_at, updated_at`,
      [
        dto.tenant_id,
        dto.email.toLowerCase(),
        password_hash,
        fullName,
        dto.status || 'active',
        dto.is_superuser || false,
      ]
    );

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

    logger.info('User created', { userId: user.id, email: user.email });
    return transformUserResponse(user);
  }

  async update(tenantId: string, userId: string, dto: UpdateUserDto): Promise<Omit<User, 'password_hash'>> {
    // Verify user exists and belongs to tenant
    const existingUser = await this.findById(tenantId, userId);

    // Check email uniqueness if changing
    if (dto.email && dto.email.toLowerCase() !== existingUser.email) {
      const emailExists = await queryOne<User>(
        'SELECT id FROM auth.users WHERE email = $1 AND id != $2',
        [dto.email.toLowerCase(), userId]
      );
      if (emailExists) {
        throw new ValidationError('El email ya está en uso');
      }
    }

    const updates: string[] = [];
    const values: any[] = [];
    let paramIndex = 1;

    if (dto.email !== undefined) {
      updates.push(`email = $${paramIndex++}`);
      values.push(dto.email.toLowerCase());
    }
    // Soportar firstName/lastName o full_name
    const fullName = buildFullName(dto.firstName, dto.lastName, dto.full_name);
    if (fullName) {
      updates.push(`full_name = $${paramIndex++}`);
      values.push(fullName);
    }
    if (dto.status !== undefined) {
      updates.push(`status = $${paramIndex++}`);
      values.push(dto.status);
    }

    if (updates.length === 0) {
      return existingUser;
    }

    updates.push(`updated_at = NOW()`);
    values.push(userId, tenantId);

    const user = await queryOne<User>(
      `UPDATE auth.users
       SET ${updates.join(', ')}
       WHERE id = $${paramIndex++} AND tenant_id = $${paramIndex}
       RETURNING id, tenant_id, email, full_name, status, is_superuser,
                 email_verified_at, last_login_at, created_at, updated_at`,
      values
    );

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

    logger.info('User updated', { userId: user.id });
    return transformUserResponse(user);
  }

  async delete(tenantId: string, userId: string): Promise<void> {
    // Soft delete by setting status to 'inactive'
    const result = await query(
      `UPDATE auth.users
       SET status = 'inactive', updated_at = NOW()
       WHERE id = $1 AND tenant_id = $2`,
      [userId, tenantId]
    );

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

    logger.info('User deleted (soft)', { userId });
  }

  async assignRole(userId: string, roleId: string): Promise<void> {
    await query(
      `INSERT INTO auth.user_roles (user_id, role_id, assigned_at)
       VALUES ($1, $2, NOW())
       ON CONFLICT (user_id, role_id) DO NOTHING`,
      [userId, roleId]
    );
    logger.info('Role assigned to user', { userId, roleId });
  }

  async removeRole(userId: string, roleId: string): Promise<void> {
    await query(
      'DELETE FROM auth.user_roles WHERE user_id = $1 AND role_id = $2',
      [userId, roleId]
    );
    logger.info('Role removed from user', { userId, roleId });
  }

  async getUserRoles(userId: string): Promise<any[]> {
    return query(
      `SELECT r.* FROM auth.roles r
       INNER JOIN auth.user_roles ur ON r.id = ur.role_id
       WHERE ur.user_id = $1`,
      [userId]
    );
  }
}

export const usersService = new UsersService();
