package com.arena.combat.service;

import com.arena.combat.dto.request.LoginUsuarioRequest;
import com.arena.combat.dto.request.RegistroUsuarioRequest;
import com.arena.combat.dto.response.EstadisticasUsuarioResponse;
import com.arena.combat.dto.response.UsuarioResponse;
import com.arena.combat.entity.Usuario;
import com.arena.combat.entity.enums.ClasePersonaje;
import com.arena.combat.repository.AuditoriaEventoRepository;
import com.arena.combat.repository.TurnoHistorialRepository;
import com.arena.combat.repository.UsuarioRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Slf4j
public class UsuarioService {

    private final UsuarioRepository usuarioRepository;
    private final TurnoHistorialRepository turnoHistorialRepository;
    private final AuditoriaEventoRepository auditoriaEventoRepository;

    @Transactional
    public UsuarioResponse registrarUsuario(RegistroUsuarioRequest request) {
        String cleanUsername = request.getUsername() != null ? request.getUsername().trim() : "Heroe_" + System.currentTimeMillis() % 10000;
        if (cleanUsername.isBlank()) {
            cleanUsername = "Heroe_" + System.currentTimeMillis() % 10000;
        }

        // Si el usuario ya existe, reutilizamos o actualizamos a su comodidad
        Optional<Usuario> usuarioExistente = usuarioRepository.findByUsername(cleanUsername);
        if (usuarioExistente.isPresent()) {
            Usuario u = usuarioExistente.get();
            if (request.getClasePersonaje() != null) {
                u.setClasePersonaje(request.getClasePersonaje());
                u = usuarioRepository.save(u);
            }
            log.info("Usuario '{}' ya existía. Sesión reanudada con comodidad.", cleanUsername);
            return mapearAUsuarioResponse(u);
        }

        // Email opcional y seguro
        String cleanEmail = (request.getEmail() != null && !request.getEmail().isBlank())
                ? request.getEmail().trim().toLowerCase()
                : cleanUsername.toLowerCase().replaceAll("[^a-z0-9]", "") + "@arena.local";

        if (usuarioRepository.existsByEmail(cleanEmail)) {
            cleanEmail = cleanUsername.toLowerCase().replaceAll("[^a-z0-9]", "") + "_" + (System.currentTimeMillis() % 10000) + "@arena.local";
        }

        // Contraseña libre y opcional
        String rawPassword = (request.getPassword() != null && !request.getPassword().isBlank())
                ? request.getPassword()
                : "123456";

        String passwordHash = hashearPassword(rawPassword);

        ClasePersonaje clase = request.getClasePersonaje() != null ? request.getClasePersonaje() : ClasePersonaje.GUERRERO;

        Usuario usuario = Usuario.builder()
                .username(cleanUsername)
                .email(cleanEmail)
                .passwordHash(passwordHash)
                .nivel(1)
                .experiencia(0)
                .partidasJugadas(0)
                .partidasGanadas(0)
                .clasePersonaje(clase)
                .build();

        usuario = usuarioRepository.save(usuario);
        log.info("Nuevo héroe forjado libremente: ID {}, Username {}, Clase {}", usuario.getId(), usuario.getUsername(), usuario.getClasePersonaje());

        return mapearAUsuarioResponse(usuario);
    }

    @Transactional
    public UsuarioResponse autenticarUsuario(LoginUsuarioRequest request) {
        String cleanUsername = request.getUsername() != null ? request.getUsername().trim() : "Heroe";
        if (cleanUsername.isBlank()) {
            cleanUsername = "Heroe";
        }

        Optional<Usuario> optUsuario = usuarioRepository.findByUsername(cleanUsername);

        // Si el usuario no existe, ¡se auto-registra al vuelo para máxima comodidad del jugador!
        if (optUsuario.isEmpty()) {
            log.info("Usuario '{}' no existía al iniciar sesión. Creando héroe automáticamente...", cleanUsername);
            ClasePersonaje clase = request.getClasePersonaje() != null ? request.getClasePersonaje() : ClasePersonaje.GUERRERO;
            RegistroUsuarioRequest reg = RegistroUsuarioRequest.builder()
                    .username(cleanUsername)
                    .password(request.getPassword())
                    .clasePersonaje(clase)
                    .build();
            return registrarUsuario(reg);
        }

        Usuario usuario = optUsuario.get();

        // Si proporcionó contraseña, verificamos; si no proporcionó o coincide, entra libremente
        if (request.getPassword() != null && !request.getPassword().isBlank()) {
            String hashIngresado = hashearPassword(request.getPassword());
            if (!usuario.getPasswordHash().equals(hashIngresado) && !usuario.getPasswordHash().equals(hashearPassword("123456"))) {
                // Para máxima comodidad, si la contraseña previa era default o no bloquea, permitimos el paso actualizando
                usuario.setPasswordHash(hashIngresado);
                usuario = usuarioRepository.save(usuario);
            }
        }

        return mapearAUsuarioResponse(usuario);
    }

    @Transactional(readOnly = true)
    public EstadisticasUsuarioResponse obtenerEstadisticas(Long usuarioId) {
        Usuario usuario = usuarioRepository.findById(usuarioId)
                .orElseThrow(() -> new IllegalArgumentException("Usuario no encontrado con ID: " + usuarioId));

        int jugadas = usuario.getPartidasJugadas();
        int ganadas = usuario.getPartidasGanadas();
        int perdidas = Math.max(0, jugadas - ganadas);
        double winRate = jugadas > 0 ? ((double) ganadas / jugadas) * 100.0 : 0.0;
        int expProximoNivel = usuario.getNivel() * 100;

        long turnosTotales = turnoHistorialRepository.countByUsuarioId(usuarioId);
        long eventosTotales = auditoriaEventoRepository.countByUsuarioId(usuarioId);

        return EstadisticasUsuarioResponse.builder()
                .usuarioId(usuario.getId())
                .username(usuario.getUsername())
                .nivel(usuario.getNivel())
                .experiencia(usuario.getExperiencia())
                .experienciaProximoNivel(expProximoNivel)
                .partidasJugadas(jugadas)
                .partidasGanadas(ganadas)
                .partidasPerdidas(perdidas)
                .porcentajeVictorias(Math.round(winRate * 10.0) / 10.0)
                .clasePersonaje(usuario.getClasePersonaje())
                .turnosJugadosTotales(turnosTotales)
                .eventosAuditoriaRegistrados(eventosTotales)
                .build();
    }

    @Transactional(readOnly = true)
    public UsuarioResponse obtenerPerfil(Long usuarioId) {
        Usuario usuario = usuarioRepository.findById(usuarioId)
                .orElseThrow(() -> new IllegalArgumentException("Usuario no encontrado con ID: " + usuarioId));
        return mapearAUsuarioResponse(usuario);
    }

    private UsuarioResponse mapearAUsuarioResponse(Usuario u) {
        return UsuarioResponse.builder()
                .id(u.getId())
                .username(u.getUsername())
                .email(u.getEmail())
                .nivel(u.getNivel())
                .experiencia(u.getExperiencia())
                .partidasJugadas(u.getPartidasJugadas())
                .partidasGanadas(u.getPartidasGanadas())
                .clasePersonaje(u.getClasePersonaje())
                .createdAt(u.getCreatedAt())
                .build();
    }

    private String hashearPassword(String rawPassword) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] encodedhash = digest.digest(rawPassword.getBytes(StandardCharsets.UTF_8));
            return HexFormat.of().formatHex(encodedhash);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Error en algoritmo de hash criptográfico", e);
        }
    }
}
