/*
 *  Copyright (c) 2025 John Ahlroos / Devsoap
 *
 *  Creative Commons Attribution-NoDerivatives 4.0 International Public License
 *
 *  By exercising the Licensed Rights (defined below), You accept and agree
 *  to be bound by the terms and conditions of this Creative Commons
 *  Attribution-NoDerivatives 4.0 International Public License ("Public
 *  License"). To the extent this Public License may be interpreted as a
 *  contract, You are granted the Licensed Rights in consideration of Your
 *  acceptance of these terms and conditions, and the Licensor grants You
 *  such rights in consideration of benefits the Licensor receives from
 *  making the Licensed Material available under these terms and
 *  conditions.
 *
 *  Refer to LICENSE file or https://creativecommons.org/licenses/by-nd/4.0 for full license.
 *
 */

package com.devsoap.tincheck.services;

import com.devsoap.tincheck.tin.TinCountry;
import com.devsoap.tincheck.tin.TinGenerator;
import com.devsoap.tincheck.routes.responses.GenerationResult;
import com.devsoap.tincheck.routes.responses.ValidationResult;
import io.github.resilience4j.micronaut.annotation.RateLimiter;
import jakarta.inject.Singleton;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

@Slf4j
@Singleton
@RequiredArgsConstructor
public class TinCheckService {

    private final List<TinCountry> countries;
    private final Random randomGenerator;

    @PostConstruct
    public void printStats() {
        log.info("Implemented country codes: {}", countries.stream()
                .map(TinCountry::getCountryCode)
                .collect(Collectors.toList()));
    }

    public ValidationResult validate(String countryCode, String value) {
        var valid = getByCountryCode(countryCode).validate(value);
        var result =  valid ? ValidationResult.VALID : ValidationResult.INVALID;
        log.info("Validated TIN {} for country {}. It is {}.", value, countryCode, result.result());
        return result;
    }

    public GenerationResult generate(String countryCode, LocalDate dateOfBirth, TinGenerator.Gender gender) {
        var value = getByCountryCode(countryCode).generate(dateOfBirth, gender, randomGenerator);
        return new GenerationResult(value, dateOfBirth, gender, 0L);
    }

    @RateLimiter(name = "tincheck-public")
    public ValidationResult validateRateLimited(String countryCode, String value) {
        return validate(countryCode, value);
    }

    @RateLimiter(name = "tincheck-public")
    public GenerationResult generateRateLimited(String countryCode, LocalDate dateOfBirth, TinGenerator.Gender gender) {
        return generate(countryCode, dateOfBirth, gender);
    }

    public TinCountry getByCountryCode(String countryCode) {
        return countries.stream()
            .filter(c -> c.getCountryCode().equalsIgnoreCase(countryCode))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException(String.format("Country \"%s\" not supported.", countryCode)));
    }

    public Map<String, String> getCountryMap() {
        return countries.stream()
                .sorted()
                .collect(Collectors.toMap(TinCountry::getCountryCode, TinCountry::getCountryName,
                        (e1,e2) -> e1, LinkedHashMap::new));
    }

    public TinCountry getDefaultCountry() {
        return countries.stream().sorted().findFirst().orElse(null);
    }

}