package com.social.media.application.service;

import java.time.LocalDateTime;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.social.media.application.exception.AuthenticationException;
import com.social.media.application.exception.RegistrationException;
import com.social.media.infrastructure.persistence.entity.UserEntity;
import com.social.media.infrastructure.persistence.entity.UserStatusEntity;
import com.social.media.infrastructure.persistence.entity.UserTypeEntity;
import com.social.media.infrastructure.persistence.repository.UserJpaRepository;
import com.social.media.infrastructure.security.JwtService;
import com.social.media.interfaces.web.dto.auth.LoginRequest;
import com.social.media.interfaces.web.dto.auth.LoginResponse;
import com.social.media.interfaces.web.dto.auth.RegisterRequest;
import com.social.media.interfaces.web.dto.user.UserResponse;

/**
 * Service for handling authentication operations
 */
@Service
@Transactional
public class AuthService {

    private final UserJpaRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtService jwtService;

    @Autowired
    public AuthService(UserJpaRepository userRepository, 
                      PasswordEncoder passwordEncoder,
                      JwtService jwtService) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.jwtService = jwtService;
    }

    /**
     * Authenticate user with email and password
     */
    public LoginResponse signin(LoginRequest request) {
        // Find user by email - using default company for now
        UserEntity user = userRepository.findByEmail(request.email())
            .orElseThrow(() -> AuthenticationException.userNotFound());

        // Account locking functionality has been removed from UserEntity

        // Check password
        if (!passwordEncoder.matches(request.password(), user.getPasswordHash())) {
            handleFailedLogin(user);
            throw AuthenticationException.invalidCredentials();
        }

        // Check user status
        if (user.getStatus() != UserStatusEntity.ACTIVE) {
            throw new AuthenticationException("Conta não está ativa. Status: " + user.getStatus());
        }

        // Check email verification
        if (!user.getEmailVerified()) {
            throw new AuthenticationException("E-mail não verificado. Verifique seu e-mail antes de fazer login.");
        }

        // Update last login time
        user.setLastAccessDate(LocalDateTime.now());
        userRepository.save(user);

        // Generate JWT tokens
        String accessToken = jwtService.generateAccessToken(user.getEmail(), user.getId(), user.getType().name());
        String refreshToken = jwtService.generateRefreshToken(user.getEmail());

        // Build response
        UserResponse userResponse = mapToUserResponse(user);
        
        return LoginResponse.builder()
            .accessToken(accessToken)
            .refreshToken(refreshToken)
            .tokenType("Bearer")
            .expiresIn(jwtService.getAccessTokenExpirationTime())
            .user(userResponse)
            .build();
    }

    /**
     * Register new user
     */
    public LoginResponse signup(RegisterRequest request) {
        // Check if email already exists - using default company for now
        if (userRepository.existsByEmailAndCompanyId(request.email(), 1L)) {
            throw RegistrationException.emailAlreadyExists();
        }

        // Create new user entity
        UserEntity user = new UserEntity();
        user.setName(request.name());
        user.setEmail(request.email());
        user.setPasswordHash(passwordEncoder.encode(request.password()));
        user.setCompanyId(request.companyId() != null ? request.companyId() : 1L); // Default company
        user.setType(mapUserType(request.type()));
        user.setStatus(UserStatusEntity.PENDING); // Requires email verification
        user.setEmailVerified(false);
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());

        // Save user
        UserEntity savedUser = userRepository.save(user);

        // For demo purposes, auto-verify email and activate user
        savedUser.setEmailVerified(true);
        savedUser.setStatus(UserStatusEntity.ACTIVE);
        userRepository.save(savedUser);

        // Generate JWT tokens
        String accessToken = jwtService.generateAccessToken(savedUser.getEmail(), savedUser.getId(), savedUser.getType().name());
        String refreshToken = jwtService.generateRefreshToken(savedUser.getEmail());

        // Build response
        UserResponse userResponse = mapToUserResponse(savedUser);
        
        return LoginResponse.builder()
            .accessToken(accessToken)
            .refreshToken(refreshToken)
            .tokenType("Bearer")
            .expiresIn(jwtService.getAccessTokenExpirationTime())
            .user(userResponse)
            .build();
    }

    /**
     * Refresh authentication token
     */
    public LoginResponse refreshToken(String refreshToken) {
        // Validate and extract email from refresh token
        String email = jwtService.extractEmailFromRefreshToken(refreshToken);
        
        // Find user
        UserEntity user = userRepository.findByEmail(email)
            .orElseThrow(() -> AuthenticationException.userNotFound());

        // Check user status
        if (user.getStatus() != UserStatusEntity.ACTIVE) {
            throw new AuthenticationException("Conta não está ativa");
        }

        // Generate new tokens
        String newAccessToken = jwtService.generateAccessToken(user.getEmail(), user.getId(), user.getType().name());
        String newRefreshToken = jwtService.generateRefreshToken(user.getEmail());

        return LoginResponse.builder()
            .accessToken(newAccessToken)
            .refreshToken(newRefreshToken)
            .tokenType("Bearer")
            .expiresIn(jwtService.getAccessTokenExpirationTime())
            .build();
    }

    /**
     * Get current user by JWT token
     */
    @Transactional(readOnly = true)
    public UserResponse getCurrentUser(String token) {
        String email = jwtService.extractEmailFromAccessToken(token);
        UserEntity user = userRepository.findByEmail(email)
            .orElseThrow(() -> AuthenticationException.userNotFound());
        
        return mapToUserResponse(user);
    }

    /**
     * Logout user and invalidate tokens
     */
    @Transactional
    public void logout(String token) {
        try {
            // Extract email from token to validate it's a valid token
            String email = jwtService.extractEmailFromAccessToken(token);
            
            // Verify user exists and token is valid
            UserEntity user = userRepository.findByEmail(email)
                .orElseThrow(() -> AuthenticationException.userNotFound());
            
            // Update last modified timestamp
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
            
            // In a production environment, you would:
            // 1. Add the token to a blacklist/blocklist stored in Redis
            // 2. Or store a token version in the user record and increment it
            // 3. Or use a shorter token expiration time with refresh tokens
            
            // For now, we just validate the token and update timestamp
            // The token will naturally expire based on its expiration time
            
        } catch (Exception e) {
            throw new RuntimeException("Invalid token for logout", e);
        }
    }

    /**
     * Handle failed login attempt - functionality removed as fields don't exist in database
     */
    private void handleFailedLogin(UserEntity user) {
        // Failed login attempt tracking removed as fields don't exist in database
        // This method kept for compatibility but doesn't perform any action
    }

    /**
     * Map string to UserTypeEntity
     */
    private UserTypeEntity mapUserType(String type) {
        if (type == null) {
            return UserTypeEntity.USER; // Default type
        }
        
        try {
            return UserTypeEntity.valueOf(type.toUpperCase());
        } catch (IllegalArgumentException e) {
            return UserTypeEntity.USER; // Default if invalid
        }
    }

    /**
     * Map UserEntity to UserResponse DTO
     */
    private UserResponse mapToUserResponse(UserEntity user) {
        return UserResponse.builder()
            .id(user.getId().toString())
            .companyId(user.getCompanyId().toString())
            .name(user.getName())
            .email(user.getEmail())
            .phone(user.getPhone())
            .whatsappEnabled(false) // Not in UserEntity, default to false
            .avatarUrl(null) // Not in UserEntity, could be added later
            .position(null) // Not in UserEntity, could be added later
            .department(null) // Not in UserEntity, could be added later
            .registrationDate(user.getCreatedAt())
            .lastAccessDate(user.getLastAccessDate())
            .status(user.getStatus().name())
            .type(mapUserTypeToString(user.getType()))
            .emailVerified(user.getEmailVerified())
            .deleted(false) // Not in UserEntity, assuming false
            .createdAt(user.getCreatedAt())
            .updatedAt(user.getUpdatedAt())
            .build();
    }

    /**
     * Map UserTypeEntity to string for response
     */
    private String mapUserTypeToString(UserTypeEntity userType) {
        return switch (userType) {
            case SUPER_ADMIN -> "SUPER_ADMIN";
            case ADMIN -> "COMPANY_ADMIN";
            case MANAGER -> "MANAGER";
            case USER -> "USER";
            case VIEWER -> "VIEWER";
        };
    }
}
