package app.controller;

import app.config.SiteSecDBData;
import app.model.Registration;
import app.model.Users;
import app.repository.UsersRepository;
import app.service.UsersEmailsService;

import ca.tecreations.db.mysql.MySQL;
import ca.tecreations.misc.TimsTime;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.ServletException;
import jakarta.validation.Valid; // "F:\projects\security1\jars\jakarta.validation-api-3.0.2.jar"

import java.io.Serializable;
import java.util.List;
import java.util.Optional;

//import javax.validation.constraints.Email;
//import javax.validation.constraints.NotEmpty;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Controller; // "F:\projects\security1\jars\spring-context-6.2.1.jar"
import org.springframework.ui.Model;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.BindingResult; // "F:\projects\security1\jars\spring-context-6.2.1.jar"
import org.springframework.validation.Errors; // "F:\projects\security1\jars\spring-context-6.2.1.jar"
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute; // "F:\projects\security1\jars\spring-web-6.2.1.jar"
import org.springframework.web.bind.annotation.PostMapping; // "F:\projects\security1\jars\spring-web-6.2.1.jar"
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@RestController
public class RegistrationController {
    public boolean debug = true;
    
    @Autowired
    public UsersRepository usersRepository;
    
    @Autowired
    DriverManagerDataSource dataSource;
     
    @Autowired
    SiteSecDBData data;
     
    @Autowired
    public PasswordEncoder passwordEncoder;


    
    @GetMapping("/activate")
    public ModelAndView activate(@RequestParam String code) 
    throws Exception {
        System.out.println("RegistrationController.activate");
        MySQL mysql = new MySQL(dataSource); 
        ModelAndView mv;
        // redirect to verified email message
        List<List<String>> entry = mysql.getRows("SELECT uid,expiry FROM account_ops WHERE txt='" + code + "'", false);
        if (entry.size() == 1) {
            String uid = entry.get(0).get(0);
            String expiry = entry.get(0).get(1);
            if (uid == null) {
                // some generated code, but non-existing, so could be hacker attempt
                throw new Exception("RegistrationController.activate: Invalid Code Use Attempt(1): " + code);
            } else { 
                if (new TimsTime().isLessThan(expiry)) {
                    mysql.issue("DELETE FROM account_ops WHERE uid='" + uid + "'");
                    mysql.issue("UPDATE users SET enabled='1' WHERE uid='" + uid + "'");
                    mv = new ModelAndView("account/activated");
                } else {
                    // activation expiry reached
                    mv = new ModelAndView("account/activation_expired");
                }
            }
        } else {
            throw new Exception("RegistrationController.activate: Invalid Code Use Attempt(2): " + code);
        }
        return mv;
    }

    @GetMapping("/register") // endpoint
    public ModelAndView registerGet(final Model model, HttpServletRequest hsr) {
        ModelAndView mv = new ModelAndView("account/register"); 
        mv.addObject("registration", new Registration());
        return mv;
    }
    
    public void addEmail(String userRegEmail, String userEmailN) {
        String sql = "SELECT uid FROM users WHERE email='" + userRegEmail + "'";
        String uid = data.getValue(sql,debug);
        sql = "INSERT INTO emails (uid,email) VALUES('" + uid + "','" + userEmailN + "')";
        data.issue(sql,debug);
    }
    
    @PostMapping("/register")
    public ModelAndView registerPost(
            @Valid @ModelAttribute("registration") Registration registration, 
            BindingResult result, 
            Model model,
            Errors errors) 
    throws Exception {
        String email = registration.getEmail();
        MySQL mysql = new MySQL(dataSource);
        System.out.println("\nRegistrationController.registerPost: email: " + email + "\n");
        ModelAndView mv = new ModelAndView("account/register");
        if (!errors.hasErrors()) {
            String sql = "SELECT COUNT(*) FROM users WHERE email='" + email + "'";
            String countString = data.getValue(sql,debug);
            if (countString != null) {
                int count = Integer.parseInt(countString);
                if (count == 0) {
                    sql = "INSERT INTO users (email,password,authorities,enabled) " +
                          "VALUES('" + email + "'," + 
                        "'" + passwordEncoder.encode(registration.getPassword()) + "'," + 
                        "'5','0')"; // disabled user, must verify email, can 
                                  // associate multiple addresses, if desired.
                    data.issue(sql,true);
                    // send email to confirm with token
                    Optional<Users> user = usersRepository.findByEmail(email);
                    if (user.isPresent() && user.get() != null) {
                        new UsersEmailsService(data).sendActivationEmail(user.get());
                    }
                    // redirect to verify email message
                    mv = new ModelAndView("account/verifyEmail");
                } else {
                    sql = "SELECT enabled FROM users WHERE email='" + email + "'";
                    boolean enabled = Boolean.parseBoolean(data.getValue(sql,debug));
                    if (!enabled) {
                        Optional<Users> user = usersRepository.findByEmail(email);
                        if (user.isPresent() && user.get() != null) {
                            new UsersEmailsService(data).sendActivationEmail(user.get());
                        }
                        mv = new ModelAndView("account/verifyEmail");
                    } else {
                        mv = new ModelAndView("account/login");
                        mv.addObject("email",email);
                    }
                }
            }
        } else {
            mv = new ModelAndView("account/register");
            mv.addObject("registration", registration);
            if (!registration.getPassword().equals(registration.getConfirmation())) {
                result.rejectValue("confirmation", "mismatch", "Password and confirmation do not match.");
                return mv;
            }
        }
        return mv;
    }

    public boolean userActivated(String email) {
        String activated = data.getValue("SELECT enabled FROM users WHERE emails='" + email + "'", debug);
        return activated.equals("1");
    }
        
    public boolean isValidEmail(String email) {
        // must have an @ and a dot for top-level domain separator
        if (email.contains("@") && email.contains(".")) {
            // does it contain more than one @?
            if (email.substring(email.indexOf('@') + 1).contains("@")) {
                return false; 
            } else if (!email.substring(email.indexOf("@") + 1).contains(".")) {
                return false;
            } 
        }
        return true;
    }
}
