package com.project.whatsappchatbot.controller;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.project.whatsappchatbot.DTO.ChangePasswordDTO;
import com.project.whatsappchatbot.DTO.LoginRequest;
import com.project.whatsappchatbot.DTO.RegistrationRequest;
import com.project.whatsappchatbot.DTO.TokenRequest;
import com.project.whatsappchatbot.constant.UserExists;
import com.project.whatsappchatbot.controller.BotController.WhatsAppBotController;
import com.project.whatsappchatbot.model.Account;
import com.project.whatsappchatbot.model.Customer;
import com.project.whatsappchatbot.model.Mechanic;
import com.project.whatsappchatbot.repository.AccountRepository;
import com.project.whatsappchatbot.repository.CustomerRepository;
import com.project.whatsappchatbot.repository.FeedbackRepository;
import com.project.whatsappchatbot.repository.InvoiceRepository;
import com.project.whatsappchatbot.repository.MechanicRepository;
import com.project.whatsappchatbot.repository.OrderRepository;
import com.project.whatsappchatbot.repository.RoleRepository;
import com.project.whatsappchatbot.response.ErrorResponse;
import com.project.whatsappchatbot.response.SuccessResponse;
import com.project.whatsappchatbot.security.jwt.JwtBlacklist;
import com.project.whatsappchatbot.security.jwt.JwtUtils;
import com.project.whatsappchatbot.service.AccountService;
import com.project.whatsappchatbot.service.OtpServiceTemplate;
import com.project.whatsappchatbot.service.SendOtpService;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

@CrossOrigin(origins = "https://wati.bikefixup.co.in", maxAge = 3600)
@RestController
@RequestMapping("/api")
public class AuthController {
	@Autowired
	private JavaMailSender emailSender;
	@Autowired
	AuthenticationManager authenticationManager;

	@Autowired
	private MechanicRepository mechanicRepository;

	@Autowired
	private OrderRepository orderRepository;

	@Autowired
	private FeedbackRepository feedbackRepository;

	@Autowired
	private AccountRepository accountRepository;
	@Autowired
	private JwtBlacklist jwtBlacklist;

	@Autowired
	RoleRepository roleRepository;

	@Autowired
	PasswordEncoder encoder;

	@Autowired
	JwtUtils jwtUtils;

	@Autowired
	private InvoiceRepository invoiceRepository;

	@Autowired
	private CustomerRepository customerRepository;

	@Autowired
	private WhatsAppBotController whatsAppBotController;

	@Autowired
	private OtpServiceTemplate otpService;
	
	@Autowired
	private AccountService accountService;

	@Autowired
	private SendOtpService sendOtpService;

	private static final Logger logger = LoggerFactory.getLogger(AdminPanelController.class);

	@PostMapping(value = "/authenticate", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<Map<String, String>> authenticateToken(@RequestBody TokenRequest tokenRequest) {
		String accessToken = tokenRequest.getAccessToken();
		String username = null;

		logger.info("Received token: {}", accessToken);

		if (jwtBlacklist.isTokenBlacklisted(accessToken)) {
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		}

		try {

			if (isValidJwtFormat(accessToken)) {
				username = jwtUtils.getUserNameFromJwtToken(accessToken);
			} else {
				logger.error("Invalid JWT token format: {}", accessToken);
				Map<String, String> response = new HashMap<>();
				response.put("status", "InvalidToken");
				return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
			}
		} catch (SignatureException e) {
			logger.error("Invalid JWT signature: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		} catch (MalformedJwtException e) {
			logger.error("Invalid JWT token: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		} catch (ExpiredJwtException e) {
			logger.error("JWT token is expired: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "ExpiredToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		} catch (UnsupportedJwtException e) {
			logger.error("JWT token is unsupported: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		} catch (IllegalArgumentException e) {
			logger.error("JWT claims string is empty: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		}

		if (username != null) {
			Map<String, String> response = new HashMap<>();
			response.put("status", "valid");
			response.put("username", username);
			return ResponseEntity.ok(response);
		} else {
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		}
	}

	private boolean isValidJwtFormat(String token) {

		return token != null && token.split("\\.").length == 3;
	}

	public class InvalidTokenException extends RuntimeException {
		public InvalidTokenException(String message) {
			super(message);
		}
	}

	@PostMapping(value = "/signin")
	public ResponseEntity<Map<String, Object>> authenticateUser(@Valid @RequestBody LoginRequest loginRequest,
			Model model) {
		Map<String, Object> responseBody = new LinkedHashMap<>();

		try {
			//check user exists
			if (accountService.checkIfUserExists(loginRequest.getPhone()) == UserExists.USER_NOT_EXISTS) {
				responseBody.put("error", "User Details not found");
				return ResponseEntity.status(HttpStatus.NOT_FOUND).body(responseBody);
			}
			if (accountService.checkIfUserExists(loginRequest.getPhone()) == UserExists.USER_EXISTS_RATE_LIMIT) {
				responseBody.put("error", "User Exists rate limit");
				return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(responseBody);
			}

			//if user exists -> get otp
			return sendOtpService.requestOtp(loginRequest, null);
//			if(!sendOtp) {
//				responseBody.put("error", "Failed to send OTP");
//				return ResponseEntity.status(HttpStatus.NOT_FOUND).body(responseBody);
//			} 
//			responseBody.put("message", "OTP sent successfully");
//			return ResponseEntity.ok(responseBody);
		} catch (AuthenticationException e) {

			System.out.println("Authentication error: " + e.getMessage());

			responseBody.put("error", "Invalid username or password");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseBody);
		}
	}

	
	@PostMapping(value = "/register")
	public ResponseEntity<Map<String, Object>> registerUser(@Valid @RequestBody LoginRequest loginRequest,
			Model model) {
		Map<String, Object> responseBody = new HashMap<>();

		try {
			//check user exists
			if (accountService.checkIfUserExists(loginRequest.getPhone()) == UserExists.USER_EXISTS) {
				responseBody.put("error", "User exists");
				return ResponseEntity.status(HttpStatus.CONFLICT).body(responseBody);
			}
			
			if (accountService.checkIfUserExists(loginRequest.getPhone()) == UserExists.USER_EXISTS_RATE_LIMIT) {
				responseBody.put("error", "User Exists rate limit");
				return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(responseBody);
			}

			//if user not exists -> get otp
			
			return sendOtpService.requestOtp(loginRequest, null);
			
//			Boolean sendOtp = sendOtpService.requestOtpStatus(loginRequest);
//			if(!sendOtp) {
//				responseBody.put("error", "Failed to send OTP");
//				return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(responseBody);
//			} 
//			responseBody.put("message", "OTP sent successfully");
//			return ResponseEntity.ok(responseBody);
		} catch (AuthenticationException e) {

			System.out.println("Authentication error: " + e.getMessage());

			responseBody.put("error", "Invalid username or password");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseBody);
		}
	}

	private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$");

	private static final Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9]+$");

	@PostMapping("/signup")
	public ResponseEntity<?> registerUser(@Valid @RequestBody RegistrationRequest signUpRequest) {
		String role = signUpRequest.getRole();
		if (role.equalsIgnoreCase("mechanic")) {

			Account account = new Account();

			account.setRole("mechanic");
			account.setStatus("active");
			accountRepository.save(account);

			Mechanic mechanic = new Mechanic();
			// mechanic.setId(account);
			mechanic.setName(signUpRequest.getFullName());
			// mechanic.setEmail(signUpRequest.getEmail());
			mechanic.setPhone(signUpRequest.getPhoneNumber());
			mechanic.setSpeciality(signUpRequest.getSpeciality());
			mechanicRepository.save(mechanic);
		} else if (role.equalsIgnoreCase("admin")) {
			if (accountRepository.existsByUsername(signUpRequest.getUsername())) {
				return ResponseEntity.badRequest().body(new ErrorResponse("Error: Username is already taken!"));
			}

			if (!isValidEmail(signUpRequest.getEmail())) {
				return ResponseEntity.badRequest().body(new ErrorResponse("Error: Invalid email address format!"));
			}

			if (!isValidUsername(signUpRequest.getUsername())) {
				return ResponseEntity.badRequest().body(new ErrorResponse(
						"Error: Username must be at least 5 characters long and contain only alphanumeric characters!"));
			}

			if (!isValidPassword(signUpRequest.getPassword())) {
				return ResponseEntity.badRequest().body(new ErrorResponse(
						"Error: Password must be at least 6 characters long and contain both alphabets and numbers!"));
			}

			Account account = new Account();
			account.setUsername(signUpRequest.getUsername());
			account.setPassword(encoder.encode(signUpRequest.getPassword()));
			account.setRole("admin");
			account.setStatus("active");
			account.setEmail(signUpRequest.getEmail());
			account.setFullName(signUpRequest.getFullName());
			accountRepository.save(account);
		} else {
			return ResponseEntity.badRequest().body(new ErrorResponse("Invalid role provided."));
		}

		return ResponseEntity.ok(new SuccessResponse("User registered successfully!"));
	}

	private boolean isValidUsername(String username) {
		return username.length() >= 5 && USERNAME_PATTERN.matcher(username).matches();
	}

	private boolean isValidEmail(String email) {
		return EMAIL_PATTERN.matcher(email).matches();
	}

	private boolean isValidPassword(String password) {
		if (password.length() < 6) {
			return false;
		}
		boolean hasAlphabet = password.matches(".*[a-zA-Z].*");
		boolean hasNumber = password.matches(".*\\d.*");
		return hasAlphabet && hasNumber;
	}

	@PutMapping("/change-password")
	public ResponseEntity<Map<String, String>> changePassword(@RequestBody ChangePasswordDTO changePasswordDTO) {
		String accessToken = changePasswordDTO.getAccessToken();
		String username = null;

		logger.info("Received token: {}", accessToken);

		if (jwtBlacklist.isTokenBlacklisted(accessToken)) {
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		}

		try {

			if (isValidJwtFormat(accessToken)) {
				username = jwtUtils.getUserNameFromJwtToken(accessToken);
				Account account = accountRepository.findByUsername(username);

				if (account != null && encoder.matches(changePasswordDTO.getOldPassword(), account.getPassword())) {
					account.setPassword(encoder.encode(changePasswordDTO.getNewPassword()));
					accountRepository.save(account);

					Map<String, String> response = new HashMap<>();
					response.put("status", "PasswordChanged");
					return ResponseEntity.ok(response);
				} else {
					Map<String, String> response = new HashMap<>();
					response.put("status", "InvalidOldPassword");
					return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
				}
			} else {
				logger.error("Invalid JWT token format: {}", accessToken);
				Map<String, String> response = new HashMap<>();
				response.put("status", "InvalidToken");
				return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
			}
		} catch (SignatureException | MalformedJwtException | ExpiredJwtException | UnsupportedJwtException
				| IllegalArgumentException e) {
			logger.error("JWT error: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		}
	}

	@PostMapping("/logout")
	public ResponseEntity<?> logout(HttpServletRequest request, HttpServletResponse response,
			@RequestBody TokenRequest tokenRequest) {
		String token = tokenRequest.getAccessToken();
		if (token != null) {
			jwtBlacklist.blacklistToken(token);

			HttpSession session = request.getSession(false);
			if (session != null) {
				session.invalidate();
			}

			Cookie jwtCookie = new Cookie("jwt", null);
			jwtCookie.setPath("/");
			jwtCookie.setMaxAge(0);
			response.addCookie(jwtCookie);

			Cookie usernameCookie = new Cookie("username", null);
			usernameCookie.setPath("/");
			usernameCookie.setMaxAge(0);
			response.addCookie(usernameCookie);

			Cookie rolesCookie = new Cookie("roles", null);
			rolesCookie.setPath("/");
			rolesCookie.setMaxAge(0);
			response.addCookie(rolesCookie);

			return ResponseEntity.ok(new SuccessResponse("Logout successful"));
		} else {
			return ResponseEntity.badRequest().body(new ErrorResponse("No token found"));
		}
	}

	@PostMapping("/customers")
	public Customer createCustomer(@RequestBody Customer customer) {
		return customerRepository.save(customer);
	}

	@GetMapping("/accounts/{accountId}")
	public Account getAccount(@PathVariable Long accountId) {
		return accountRepository.findById(accountId).orElse(null);
	}

	@GetMapping("/customers/{customerId}")
	public Customer getCustomer(@PathVariable Long customerId) {
		return customerRepository.findById(customerId).orElse(null);
	}

	@PutMapping("/accounts/{accountId}")
	public Account updateAccount(@PathVariable Long accountId, @RequestBody Account updatedAccount) {
		Account account = accountRepository.findById(accountId).orElse(null);
		if (account != null) {

			account.setUsername(updatedAccount.getUsername());
			account.setPassword(updatedAccount.getPassword());
			return accountRepository.save(account);
		}
		return null;
	}

	@PostMapping(value = "/profile", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<Map<String, String>> profile(@RequestBody TokenRequest tokenRequest) {
		String accessToken = tokenRequest.getAccessToken();
		String username = null;

		logger.info("Received token: {}", accessToken);

		if (jwtBlacklist.isTokenBlacklisted(accessToken)) {
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		}

		try {

			if (isValidJwtFormat(accessToken)) {
				username = jwtUtils.getUserNameFromJwtToken(accessToken);
			} else {
				logger.error("Invalid JWT token format: {}", accessToken);
				Map<String, String> response = new HashMap<>();
				response.put("status", "InvalidToken");
				return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
			}
		} catch (SignatureException e) {
			logger.error("Invalid JWT signature: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		} catch (MalformedJwtException e) {
			logger.error("Invalid JWT token: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		} catch (ExpiredJwtException e) {
			logger.error("JWT token is expired: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "ExpiredToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		} catch (UnsupportedJwtException e) {
			logger.error("JWT token is unsupported: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		} catch (IllegalArgumentException e) {
			logger.error("JWT claims string is empty: {}", e.getMessage());
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		}

		if (username != null) {
			Map<String, String> response = new HashMap<>();

			Account account = accountRepository.findByUsername(username);

			response.put("fullName", account.getFullName());
			return ResponseEntity.ok(response);
		} else {
			Map<String, String> response = new HashMap<>();
			response.put("status", "InvalidToken");
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
		}
	}

	@PutMapping("/customers/{customerId}")
	public Customer updateCustomer(@PathVariable Long customerId, @RequestBody Customer updatedCustomer) {
		Customer customer = customerRepository.findById(customerId).orElse(null);
		if (customer != null) {
			customer.setName(updatedCustomer.getName());
			// customer.setEmail(updatedCustomer.getEmail());
			customer.setPhone(updatedCustomer.getPhone());
			// customer.setAddress(updatedCustomer.getAddress());
			return customerRepository.save(customer);
		}
		return null;
	}

}
