/*
 *  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.countries;

import com.devsoap.tincheck.tin.TinCountry;
import io.micronaut.core.util.StringUtils;
import jakarta.inject.Singleton;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;

import java.time.LocalDate;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Getter
@Singleton
@RequiredArgsConstructor
@ToString(of = {"countryCode", "countryName"})
public class Country_EE implements TinCountry {

    private final String countryCode = "EE";
    private final String countryName = "Estonia";

    private final int[] WEIGHTS = {1,2,3,4,5,6,7,8,9,1};
    private final int[] ALT_WEIGHTS = {3,4,5,6,7,8,9,1,2,3};

    @Override
    public String generate(LocalDate yearOfBirth, Gender gender, Random randomGenerator) {

        var digits = new int[11];
        digits[0] = randomGenerator.nextInt(6) + 1;
        digits[2] = yearOfBirth.getYear() % 10;
        digits[1] = (yearOfBirth.getYear() % 100 - digits[2]) / 10;
        digits[3] = yearOfBirth.getMonthValue() / 10;
        digits[4] = yearOfBirth.getMonthValue() % 10;
        digits[5] = yearOfBirth.getDayOfMonth() / 10;
        digits[6] = yearOfBirth.getDayOfMonth() % 10;

        var c8910 = randomGenerator.nextInt(711);
        digits[7] = c8910 / 100;
        digits[8] = c8910 % 100 / 10;
        digits[9] = c8910 % 10;

        digits[10] = calculateCheckSum(digits);

        return IntStream.of(digits).mapToObj(String::valueOf).collect(Collectors.joining());
    }

    @Override
    public boolean formatCheck(String tin) {
        if (tin.length() != 11) {
            return false;
        }
        if (!StringUtils.isDigits(tin)) {
            return false;
        }
        return true;
    }

    @Override
    public boolean logicCheck(String tin) {
        var digits = extractDigits(tin);
        var checksum = calculateCheckSum(digits);
        return checksum == digits[10];
    }

    private int calculateCheckSum(int[] digits) {
        var sum = IntStream.range(0, 9).map(i -> digits[i] * WEIGHTS[i]).sum();
        var checksum = sum % 11;

        if (checksum == 10) {
            sum = IntStream.range(0, 9).map(i -> digits[i] * ALT_WEIGHTS[i]).sum();
            checksum = sum % 11;
        }

        if (checksum == 10) {
            checksum = 0;
        }
        return checksum;
    }
}