package ru.kayashov.bar.controller;

import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.kayashov.bar.controller.dto.AuthRequestDto;
import ru.kayashov.bar.controller.dto.AuthResponseDto;
import ru.kayashov.bar.controller.dto.VisitorResponseDto;
import ru.kayashov.bar.model.entity.Visitor;
import ru.kayashov.bar.repository.VisitorsRepository;
import ru.kayashov.bar.security.JwtTokenProvider;
import ru.kayashov.bar.service.VisitorService;

import javax.annotation.security.PermitAll;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Optional;

@Slf4j
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

    private final JwtTokenProvider jwtTokenProvider;
    private final VisitorsRepository visitorsRepository;
    private final PasswordEncoder passwordEncoder;
    private final VisitorService visitorService;

    @PermitAll
    @PostMapping("/login")
    public AuthResponseDto checkTelegramChat(@RequestBody AuthRequestDto dto) {
        if (dto.getByLogin()) {
            return checkLogin(dto.getLogin(), dto.getPassword());
        } else {
            return parseCode(dto.getCode());
        }
    }

    @PermitAll
    @PostMapping("refresh")
    public AuthResponseDto refreshToken(@RequestHeader("Authorization") String token) {
        Claims claims = jwtTokenProvider.extractAllClaims(token);
        Long visitorId = claims.get("id", Long.class);
        log.info("обновление токена для пользователя {}", visitorId);
        Visitor visitor = visitorsRepository.findById(visitorId).orElseThrow();
        return new AuthResponseDto(jwtTokenProvider.generateToken(visitor), null);
    }

    @GetMapping("/getMe")
    public VisitorResponseDto getMe() {
        Visitor visitor = visitorService.getCurrentVisitor();
        VisitorResponseDto dto = VisitorResponseDto.mapToDto(visitor, true, visitor.getRole().toString(), true);
        log.info("Запрос информации о пользователе: {}-{} {}, вошел в бар",
                dto.getId(),
                dto.getName().strip(),
                dto.getLastName() != null ? dto.getLastName().strip() : "");
        return dto;
    }

    private AuthResponseDto checkLogin(String login, String password) {
        if (login == null || login.isEmpty() || password == null || password.isEmpty()) {
            return new AuthResponseDto(null, "Поля не могут быть пустые");
        }

        Optional<Visitor> visitorOpt = visitorsRepository.findByLogin(login);
        if (visitorOpt.isEmpty()) {
            return new AuthResponseDto(null, "Не найдет пользователь " + login);
        }
        Visitor visitor = visitorOpt.get();
        log.info("Попытка авторизации пользователя {}", visitor.getId());
        if (passwordEncoder.matches(password, visitor.getPassword())) {
            return new AuthResponseDto(jwtTokenProvider.generateToken(visitor), null);
        } else {
            return new AuthResponseDto(null, "Неверный логин или пароль");
        }
    }

    private AuthResponseDto parseCode(String code) {
        String decode = new String(Base64.getDecoder().decode(code), StandardCharsets.UTF_8);
        String[] decodeArr = decode.split(":");
        Visitor visitor = visitorsRepository.findById(Long.valueOf(decodeArr[0]))
                .orElseThrow();

        log.info("Попытка авторизации пользователя {}", visitor.getId());
        Integer visitorCode = visitor.getCode();
        if (visitorCode == null) {
            return new AuthResponseDto(null, "Повторите запрос кода из бота");
        }
        if (Integer.valueOf(decodeArr[1]).equals(visitor.getCode())) {
            visitor.setCode(null);
            visitor = visitorsRepository.save(visitor);
            return new AuthResponseDto(jwtTokenProvider.generateToken(visitor), null);
        }
        return new AuthResponseDto(null, "Неверный код подтверждения");
    }

}
