package ru.kirillius.pf.sdn.flow;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ru.kirillius.pf.sdn.api.Networking.IPv4Subnet;
import ru.kirillius.pf.sdn.api.Networking.NetworkScope;
import ru.kirillius.pf.sdn.api.flow.CallContext;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@RequiredArgsConstructor

public class ResourceFilter extends CommonFunction {

    @Override
    public List<String> getInputNames() {
        return List.of("input", "filter");
    }

    @Override
    public List<String> getOutputNames() {
        return List.of("output", "filtered");
    }

    @Override
    public void apply(CallContext context) {
        var output = new NetworkScope();
        var filtered = new NetworkScope();
        var source = context.getSources().get(0);
        var filter = context.getSources().get(1);

        if (!filter.getSubnets().isEmpty()) {
            filterSubnets(source, filter, output, filtered);
        } else {
            output.setSubnets(source.getSubnets());
        }
        if (!filter.getASN().isEmpty()) {
            filterASN(source, filter, output, filtered);
        } else {
            output.setASN(source.getASN());
        }
        if (!filter.getDomains().isEmpty()) {
            filterDomains(source, filter, output, filtered);
        } else {
            output.setDomains(source.getDomains());
        }
        if (!filter.getResolveDomains().isEmpty()) {
            filterResolveDomains(source, filter, output, filtered);
        } else {
            output.setResolveDomains(source.getResolveDomains());
        }

        context.setOutputs(List.of(output, filtered));
    }

    private void filterSubnets(NetworkScope source, NetworkScope filter, NetworkScope output, NetworkScope filtered) {
        var sourceSubnets = source.getSubnets();
        var filterSubnets = filter.getSubnets();
        var outputSubnets = new ArrayList<IPv4Subnet>();
        var filteredSubnets = new ArrayList<IPv4Subnet>();

        sourceSubnets.forEach(subnet -> {
            if (!filterSubnets.contains(subnet)) {
                outputSubnets.add(subnet);
            } else {
                filteredSubnets.add(subnet);
            }
        });

        output.setSubnets(outputSubnets);
        filtered.setSubnets(filteredSubnets);
    }

    private void filterASN(NetworkScope source, NetworkScope filter, NetworkScope output, NetworkScope filtered) {
        var sourceASN = source.getASN();
        var filterASN = filter.getASN();
        var outputASN = new ArrayList<Integer>();
        var filteredASN = new ArrayList<Integer>();
        sourceASN.forEach(asn -> {
            if (!filterASN.contains(asn)) {
                outputASN.add(asn);
            } else {
                filteredASN.add(asn);
            }
        });
        output.setASN(outputASN);
        filtered.setASN(filteredASN);
    }

    private void filterDomains(NetworkScope source, NetworkScope filter, NetworkScope output, NetworkScope filtered) {
        var sourceDomains = source.getDomains();
        var filterDomains = filter.getDomains();
        var outputDomains = new ArrayList<String>();
        var filteredDomains = new ArrayList<String>();
        sourceDomains.forEach(domain -> {
            if (!filterDomains.contains(domain)) {
                outputDomains.add(domain);
            } else {
                filteredDomains.add(domain);
            }
        });
        output.setDomains(outputDomains);
        filtered.setDomains(filteredDomains);
    }

    private void filterResolveDomains(NetworkScope source, NetworkScope filter, NetworkScope output, NetworkScope filtered) {
        var sourceDomains = source.getResolveDomains();
        var filterDomains = filter.getResolveDomains();
        var outputDomains = new ArrayList<String>();
        var filteredDomains = new ArrayList<String>();
        sourceDomains.forEach(domain -> {
            if (!filterDomains.contains(domain)) {
                outputDomains.add(domain);
            } else {
                filteredDomains.add(domain);
            }
        });
        output.setResolveDomains(outputDomains);
        filtered.setResolveDomains(filteredDomains);
    }
}
