package org.raddatz.familienarchiv.document.annotation;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import org.junit.jupiter.api.Test;
import org.raddatz.familienarchiv.document.annotation.CreateAnnotationDTO;

import java.util.List;
import java.util.Set;

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

class UniquePointsValidatorTest {

    private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

    @Test
    void shouldAcceptNull() {
        var dto = new CreateAnnotationDTO();
        dto.setPolygon(null);

        Set<ConstraintViolation<CreateAnnotationDTO>> violations = validator.validate(dto);

        assertThat(violations).noneMatch(v -> v.getPropertyPath().toString().equals("polygon"));
    }

    @Test
    void shouldAcceptFourUniquePoints() {
        var dto = new CreateAnnotationDTO();
        dto.setPolygon(List.of(
                List.of(0.1, 0.1),
                List.of(0.9, 0.1),
                List.of(0.9, 0.9),
                List.of(0.1, 0.9)));

        Set<ConstraintViolation<CreateAnnotationDTO>> violations = validator.validate(dto);

        assertThat(violations).noneMatch(v -> v.getPropertyPath().toString().equals("polygon"));
    }

    @Test
    void shouldRejectDuplicatePoints() {
        var dto = new CreateAnnotationDTO();
        dto.setPolygon(List.of(
                List.of(0.1, 0.1),
                List.of(0.1, 0.1),
                List.of(0.9, 0.9),
                List.of(0.1, 0.9)));

        Set<ConstraintViolation<CreateAnnotationDTO>> violations = validator.validate(dto);

        assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().equals("polygon"));
    }

    @Test
    void shouldRejectPolygonWithThreePoints() {
        var dto = new CreateAnnotationDTO();
        dto.setPolygon(List.of(
                List.of(0.1, 0.1),
                List.of(0.9, 0.1),
                List.of(0.9, 0.9)));

        Set<ConstraintViolation<CreateAnnotationDTO>> violations = validator.validate(dto);

        assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().equals("polygon"));
    }

    @Test
    void shouldRejectPolygonWithFivePoints() {
        var dto = new CreateAnnotationDTO();
        dto.setPolygon(List.of(
                List.of(0.1, 0.1),
                List.of(0.5, 0.1),
                List.of(0.9, 0.1),
                List.of(0.9, 0.9),
                List.of(0.1, 0.9)));

        Set<ConstraintViolation<CreateAnnotationDTO>> violations = validator.validate(dto);

        assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().equals("polygon"));
    }

    @Test
    void shouldRejectCoordinateOutOfRange() {
        var dto = new CreateAnnotationDTO();
        dto.setPolygon(List.of(
                List.of(1.5, 0.1),
                List.of(0.9, 0.1),
                List.of(0.9, 0.9),
                List.of(0.1, 0.9)));

        Set<ConstraintViolation<CreateAnnotationDTO>> violations = validator.validate(dto);

        assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().contains("polygon"));
    }

    @Test
    void shouldRejectNegativeCoordinate() {
        var dto = new CreateAnnotationDTO();
        dto.setPolygon(List.of(
                List.of(-0.1, 0.1),
                List.of(0.9, 0.1),
                List.of(0.9, 0.9),
                List.of(0.1, 0.9)));

        Set<ConstraintViolation<CreateAnnotationDTO>> violations = validator.validate(dto);

        assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().contains("polygon"));
    }

    @Test
    void shouldRejectPointWithOneCoordinate() {
        var dto = new CreateAnnotationDTO();
        dto.setPolygon(List.of(
                List.of(0.1),
                List.of(0.9, 0.1),
                List.of(0.9, 0.9),
                List.of(0.1, 0.9)));

        Set<ConstraintViolation<CreateAnnotationDTO>> violations = validator.validate(dto);

        assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().contains("polygon"));
    }
}
