package com.project.whatsappchatbot.service;

import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.stereotype.Service;

import com.project.whatsappchatbot.DTO.LoginRequest;
import com.project.whatsappchatbot.DTO.OTPDetails;
import com.project.whatsappchatbot.model.Account;
import com.project.whatsappchatbot.repository.AccountRepository;
import com.project.whatsappchatbot.security.jwt.JwtUtils;

@Service
public class SendOtpService {

    private final Map<String, Integer> otpCountByPhone = new HashMap<>();
    private final Map<String, Long> cooldownEndTimeByPhone = new HashMap<>();
    private final Map<String, List<Long>> burstHistoryByPhone = new HashMap<>();
    private final Map<String, Long> fullBlockEndTimeByPhone = new HashMap<>();

    @Value("${max.otp}")
    private int MAX_OTPS;

    @Value("${max.bursts.per.day}")
    private int MAX_BURSTS_PER_DAY;

    @Value("${three.hours}")
    private long THREE_HOURS;

    @Value("${twenty.four.hour}")
    private long TWENTY_FOUR_HOURS;

    @Value("${excluded.phones}")
    private String excludedPhones;

    private List<String> excludedPhonesList;

    @Autowired
    private AccountRepository accountRepository;

    @Autowired
    private OtpServiceTemplate otpServiceTemplate;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    JwtUtils jwtUtils;

    OTPDetails otpDetails = new OTPDetails();

    public static final Map<String, OTPDetails> otpMap = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        if (excludedPhones != null && !excludedPhones.trim().isEmpty()) {
            excludedPhonesList = List.of(excludedPhones.split(","));
        } else {
            excludedPhonesList = new ArrayList<>();
        }
        System.out.println("excludedPhonesList: ");
        excludedPhonesList.forEach(p->System.out.println(p + " - "));
    }
    
    
    public synchronized boolean requestOtpStatus(LoginRequest loginRequest) {

        Map<String, Object> response = new LinkedHashMap<>();

        long now = System.currentTimeMillis();
        String phone = loginRequest.getPhone();

        if (phone == null || phone.trim().isEmpty()) {
            return false;
        }
        
        if (!excludedPhonesList.contains(phone)
        && fullBlockEndTimeByPhone.containsKey(phone)
        && fullBlockEndTimeByPhone.get(phone) > now) {
            return false;
        }

        if (!excludedPhonesList.contains(phone) && cooldownEndTimeByPhone.containsKey(phone)
                && cooldownEndTimeByPhone.get(phone) > now) {
        	return false;
        }

        burstHistoryByPhone.putIfAbsent(phone, new ArrayList<>());
        otpCountByPhone.putIfAbsent(phone, 0);

        List<Long> burstList = burstHistoryByPhone.get(phone);
        burstList.removeIf(ts -> now - ts > TWENTY_FOUR_HOURS);

        if (!excludedPhonesList.contains(phone) && burstList.size() >= MAX_BURSTS_PER_DAY) {
            fullBlockEndTimeByPhone.put(phone, now + TWENTY_FOUR_HOURS);
            otpCountByPhone.remove(phone);
            cooldownEndTimeByPhone.remove(phone);
            response.put("success", false);
            response.put("error", "Too many OTP bursts. This phone number is blocked for 24 hours.");
            return false;
        }

        int count = otpCountByPhone.get(phone) + 1;
        otpCountByPhone.put(phone, count);
        if (count == 1) {
            burstList.add(now);
        }

        if (count >= MAX_OTPS) {
            cooldownEndTimeByPhone.put(phone, now + THREE_HOURS);
            otpCountByPhone.put(phone, 0);
        }

        String otp = generateOTP();
        try {
//            Optional<Account> account = accountRepository.findByPhone(phone);

            otpMap.put(phone, new OTPDetails(otp, LocalDateTime.now().plusMinutes(5)));
            otpServiceTemplate.sendOtpMessage(phone, otp);

            response.put("success", true);
            response.put("message", "OTP sent successfully");
            return true;

        } catch (Exception e) {
            response.put("success", false);
            response.put("error", "Failed to send OTP");
            return false;
        }
    }
    
    

    public synchronized ResponseEntity<Map<String, Object>> requestOtp(LoginRequest loginRequest,
            HttpServletRequest request) {

        Map<String, Object> response = new LinkedHashMap<>();

        long now = System.currentTimeMillis();
        String phone = loginRequest.getPhone();

        if (phone == null || phone.trim().isEmpty()) {
            response.put("success", false);
            response.put("error", "Invalid phone number");
            return ResponseEntity.badRequest().body(response);
        }

        if (!excludedPhonesList.contains(phone)
        && fullBlockEndTimeByPhone.containsKey(phone)
        && fullBlockEndTimeByPhone.get(phone) > now) {
            response.put("success", false);
            response.put("error", "Too many OTP attempts. This phone number is blocked for 24 hours.");
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(response);
        }

        if (!excludedPhonesList.contains(phone) && cooldownEndTimeByPhone.containsKey(phone)
                && cooldownEndTimeByPhone.get(phone) > now) {
            response.put("success", false);
            response.put("error", "OTP limit reached. Try again after 3 hours.");
            return ResponseEntity.status(HttpStatus.LOCKED).body(response);
        }

        burstHistoryByPhone.putIfAbsent(phone, new ArrayList<>());
        otpCountByPhone.putIfAbsent(phone, 0);

        List<Long> burstList = burstHistoryByPhone.get(phone);
        burstList.removeIf(ts -> now - ts > TWENTY_FOUR_HOURS);

        if (!excludedPhonesList.contains(phone) && burstList.size() >= MAX_BURSTS_PER_DAY) {
            fullBlockEndTimeByPhone.put(phone, now + TWENTY_FOUR_HOURS);
            otpCountByPhone.remove(phone);
            cooldownEndTimeByPhone.remove(phone);
            response.put("success", false);
            response.put("error", "Too many OTP bursts. This phone number is blocked for 24 hours.");
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(response);
        }

        int count = otpCountByPhone.get(phone) + 1;
        otpCountByPhone.put(phone, count);
        if (count == 1) {
            burstList.add(now);
        }

        if (count >= MAX_OTPS) {
            cooldownEndTimeByPhone.put(phone, now + THREE_HOURS);
            otpCountByPhone.put(phone, 0);
        }

        String otp = generateOTP();
        try {
            otpMap.put(phone, new OTPDetails(otp, LocalDateTime.now().plusMinutes(5)));
            
            System.out.println("sendOtpMessage: " + phone + " otp: " + otp);
            otpServiceTemplate.sendOtpMessage(phone, otp);

            response.put("success", true);
            response.put("message", "OTP sent successfully");
            return ResponseEntity.ok(response);

        } catch (Exception e) {
        	e.printStackTrace();
        	System.out.println("exception: " + e.getMessage());
            response.put("success", false);
            response.put("error", "Failed to send OTP");
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
        }
    }

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