package com.example.todo;

import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import static org.springframework.http.HttpStatus.*;

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    public record Credentials(@NotBlank @Email @Size(max=254) String email,
        @NotBlank @Size(min=8,max=72) String password) {}
    public record TokenResponse(String accessToken,String tokenType,long expiresIn,String email) {}
    private final UserRepository users;
    private final PasswordEncoder passwords;
    private final JwtEncoder encoder;
    private final String issuer,audience;
    private final long ttl;
    private final String dummyHash;
    public AuthController(UserRepository users,PasswordEncoder passwords,JwtEncoder encoder,
        @Value("${app.jwt.issuer}") String issuer,@Value("${app.jwt.audience}") String audience,
        @Value("${app.jwt.ttl-seconds}") long ttl) {
        this.users=users; this.passwords=passwords; this.encoder=encoder;
        this.issuer=issuer; this.audience=audience; this.ttl=ttl;
        this.dummyHash=passwords.encode("dummy-password-not-an-account");
    }
    private String email(Credentials c) { return c.email().strip().toLowerCase(Locale.ROOT); }
    private void checkPasswordBytes(String password) {
        if(password.getBytes(StandardCharsets.UTF_8).length>72)
            throw new ResponseStatusException(BAD_REQUEST,"Password must be at most 72 UTF-8 bytes");
    }
    @PostMapping("/register") public ResponseEntity<Map<String,String>> register(@Valid @RequestBody Credentials c) {
        checkPasswordBytes(c.password());
        users.save(new AppUser(email(c),passwords.encode(c.password())));
        return ResponseEntity.status(CREATED).body(Map.of("message","Registered. Sign in now."));
    }
    @PostMapping("/login") public TokenResponse login(@Valid @RequestBody Credentials c) {
        checkPasswordBytes(c.password());
        AppUser user=users.findByEmail(email(c)).orElse(null);
        boolean matches=passwords.matches(c.password(),user==null ? dummyHash : user.getPasswordHash());
        if(user==null || !matches) throw new ResponseStatusException(UNAUTHORIZED,"Invalid email or password");
        Instant now=Instant.now();
        JwtClaimsSet claims=JwtClaimsSet.builder().issuer(issuer).subject(user.getId())
            .audience(List.of(audience)).issuedAt(now).expiresAt(now.plusSeconds(ttl)).build();
        String token=encoder.encode(JwtEncoderParameters.from(
            JwsHeader.with(MacAlgorithm.HS256).build(),claims)).getTokenValue();
        return new TokenResponse(token,"Bearer",ttl,user.getEmail());
    }
}
