package com.magistr.app.config.auth;

import com.magistr.app.model.Role;
import com.magistr.app.model.User;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;

import static org.assertj.core.api.Assertions.assertThat;

class JwtTokenServiceTest {

    @Test
    void authenticatesValidTokenForSameTenant() {
        JwtTokenService service = new JwtTokenService(properties("12345678901234567890123456789012", Duration.ofMinutes(15)));
        String token = service.createAccessToken(user(), "magistr");

        var authenticated = service.authenticate(token, "magistr");

        assertThat(authenticated).isPresent();
        assertThat(authenticated.get().id()).isEqualTo(10L);
        assertThat(authenticated.get().username()).isEqualTo("admin");
        assertThat(authenticated.get().role()).isEqualTo(Role.ADMIN);
        assertThat(authenticated.get().departmentId()).isEqualTo(2L);
    }

    @Test
    void rejectsTokenForDifferentTenant() {
        JwtTokenService service = new JwtTokenService(properties("12345678901234567890123456789012", Duration.ofMinutes(15)));
        String token = service.createAccessToken(user(), "magistr");

        assertThat(service.authenticate(token, "n8n")).isEmpty();
    }

    @Test
    void rejectsTokenWithDifferentSignature() {
        JwtTokenService issuer = new JwtTokenService(properties("12345678901234567890123456789012", Duration.ofMinutes(15)));
        JwtTokenService verifier = new JwtTokenService(properties("abcdefghijklmnopqrstuvwxyz123456", Duration.ofMinutes(15)));
        String token = issuer.createAccessToken(user(), "magistr");

        assertThat(verifier.authenticate(token, "magistr")).isEmpty();
    }

    @Test
    void rejectsExpiredToken() {
        String secret = "12345678901234567890123456789012";
        JwtTokenService service = new JwtTokenService(properties(secret, Duration.ofMinutes(15)));
        String token = expiredToken(secret);

        assertThat(service.authenticate(token, "magistr")).isEmpty();
    }

    private String expiredToken(String secret) {
        try {
            Instant now = Instant.now();
            JWTClaimsSet claims = new JWTClaimsSet.Builder()
                    .issuer("magistr")
                    .subject("10")
                    .jwtID("expired")
                    .issueTime(Date.from(now.minusSeconds(120)))
                    .expirationTime(Date.from(now.minusSeconds(90)))
                    .claim("tenant", "magistr")
                    .claim("userId", 10L)
                    .claim("username", "admin")
                    .claim("role", "ADMIN")
                    .claim("departmentId", 2L)
                    .build();
            SignedJWT jwt = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claims);
            jwt.sign(new MACSigner(secret.getBytes(StandardCharsets.UTF_8)));
            return jwt.serialize();
        } catch (JOSEException e) {
            throw new IllegalStateException(e);
        }
    }

    private JwtProperties properties(String secret, Duration accessTtl) {
        JwtProperties properties = new JwtProperties();
        properties.setSecret(secret);
        properties.setAccessTtl(accessTtl);
        properties.setRefreshTtl(Duration.ofDays(7));
        return properties;
    }

    private User user() {
        User user = new User();
        user.setId(10L);
        user.setUsername("admin");
        user.setRole(Role.ADMIN);
        user.setDepartmentId(2L);
        user.setFullName("Администратор");
        user.setJobTitle("Администратор");
        user.setPassword("hash");
        return user;
    }
}
