package com.evseminar.service;

import com.evseminar.dto.*;
import com.evseminar.exception.BadRequestException;
import com.evseminar.exception.ResourceNotFoundException;
import com.evseminar.model.Customer;
import com.evseminar.model.LoginLog;
import com.evseminar.model.VerificationCode;
import com.evseminar.model.enums.CustomerType;
import com.evseminar.model.enums.Role;
import com.evseminar.repository.CustomerRepository;
import com.evseminar.repository.LoginLogRepository;
import com.evseminar.repository.VerificationCodeRepository;
import com.evseminar.security.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class AuthService {

    private final CustomerRepository customerRepository;
    private final VerificationCodeRepository verificationCodeRepository;
    private final LoginLogRepository loginLogRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtTokenProvider jwtTokenProvider;
    private final EmailService emailService;

    @Transactional
    public ApiResponse register(RegisterRequest request) {
        CustomerType type = CustomerType.valueOf(request.getCustomerType().toUpperCase());
        if (type == CustomerType.COMPANY && (request.getCompanyName() == null || request.getCompanyName().isBlank())) {
            throw new BadRequestException("Company name is required for company registration");
        }

        // If email already exists and verified → reject
        // If email exists but NOT verified → allow resend (update info + send new code)
        if (customerRepository.existsByEmail(request.getEmail())) {
            Customer existing = customerRepository.findByEmail(request.getEmail())
                    .orElseThrow(() -> new BadRequestException("Email already registered"));
            if (existing.getEmailVerified()) {
                throw new BadRequestException("Email already registered");
            }
            existing.setName(request.getName());
            existing.setTelephone(request.getTelephone());
            existing.setPassword(passwordEncoder.encode(request.getPassword()));
            existing.setCustomerType(type);
            existing.setCompanyName(request.getCompanyName());
            customerRepository.save(existing);

            String newCode = generateVerificationCode();
            VerificationCode resendVc = VerificationCode.builder()
                    .email(request.getEmail())
                    .code(newCode)
                    .expiresAt(LocalDateTime.now().plusMinutes(10))
                    .used(false)
                    .build();
            verificationCodeRepository.save(resendVc);
            emailService.sendVerificationCode(request.getEmail(), newCode);
            return ApiResponse.success("Verification code resent to " + request.getEmail());
        }

        Customer customer = Customer.builder()
                .name(request.getName())
                .email(request.getEmail())
                .telephone(request.getTelephone())
                .password(passwordEncoder.encode(request.getPassword()))
                .customerType(type)
                .companyName(request.getCompanyName())
                .emailVerified(false)
                .role(Role.USER)
                .build();
        customerRepository.save(customer);

        String code = generateVerificationCode();
        VerificationCode vc = VerificationCode.builder()
                .email(request.getEmail())
                .code(code)
                .expiresAt(LocalDateTime.now().plusMinutes(10))
                .used(false)
                .build();
        verificationCodeRepository.save(vc);

        emailService.sendVerificationCode(request.getEmail(), code);

        return ApiResponse.success("Verification code sent to " + request.getEmail());
    }

    @Transactional
    public ApiResponse verifyEmail(VerifyEmailRequest request) {
        VerificationCode vc = verificationCodeRepository
                .findTopByEmailAndUsedFalseAndExpiresAtAfterOrderByCreatedAtDesc(
                        request.getEmail(), LocalDateTime.now())
                .orElseThrow(() -> new BadRequestException("Invalid or expired verification code"));

        if (vc.getAttempts() >= 5) {
            throw new BadRequestException("Too many failed attempts. Please request a new code.");
        }

        if (!vc.getCode().equals(request.getCode())) {
            vc.setAttempts(vc.getAttempts() + 1);
            verificationCodeRepository.save(vc);
            int remaining = 5 - vc.getAttempts();
            throw new BadRequestException("Invalid code. " + remaining + " attempt(s) remaining.");
        }

        vc.setUsed(true);
        verificationCodeRepository.save(vc);

        Customer customer = customerRepository.findByEmail(request.getEmail())
                .orElseThrow(() -> new ResourceNotFoundException("Customer not found"));
        customer.setEmailVerified(true);
        customerRepository.save(customer);

        emailService.sendWelcome(customer.getEmail(), customer.getName());

        return ApiResponse.success("Email verified successfully");
    }

    @Transactional
    public LoginResponse login(LoginRequest request, String ipAddress, String userAgent) {
        Customer customer = customerRepository.findByEmail(request.getEmail()).orElse(null);

        if (customer == null || !passwordEncoder.matches(request.getPassword(), customer.getPassword())) {
            if (customer != null) {
                saveLoginLog(customer.getCustomerId(), ipAddress, userAgent, false, "WRONG_PASSWORD");
            }
            throw new BadRequestException("Invalid email or password");
        }

        if (!customer.getEmailVerified()) {
            saveLoginLog(customer.getCustomerId(), ipAddress, userAgent, false, "NOT_VERIFIED");
            throw new BadRequestException("Please verify your email first");
        }

        customer.setLastLoginAt(LocalDateTime.now());
        customerRepository.save(customer);
        saveLoginLog(customer.getCustomerId(), ipAddress, userAgent, true, null);

        String token = jwtTokenProvider.generateToken(
                customer.getCustomerId(), customer.getEmail(), customer.getRole().name());
        return new LoginResponse(token, CustomerDTO.from(customer));
    }

    private void saveLoginLog(Long customerId, String ipAddress, String userAgent,
                               boolean success, String failReason) {
        loginLogRepository.save(LoginLog.builder()
                .customerId(customerId)
                .ipAddress(ipAddress)
                .userAgent(userAgent != null && userAgent.length() > 500
                        ? userAgent.substring(0, 500) : userAgent)
                .success(success)
                .failReason(failReason)
                .build());
    }

    public CustomerDTO getCurrentCustomer(Long customerId) {
        Customer customer = customerRepository.findById(customerId)
                .orElseThrow(() -> new ResourceNotFoundException("Customer not found"));
        return CustomerDTO.from(customer);
    }

    private String generateVerificationCode() {
        SecureRandom random = new SecureRandom();
        int code = 100000 + random.nextInt(900000);
        return String.valueOf(code);
    }
}
