package fr.uge.concurrence.exo2;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;

public class Factorio {

    public sealed interface Ore permits IronOre, CopperOre {
    }

    /**
     * IronOre is a record that represents an iron ore sample.
     */
    public record IronOre(long sampleId) implements Ore {
        private static IronOre random() {
            return new IronOre(ThreadLocalRandom.current().nextLong(1000000));
        }
    }

    /**
     * CopperOre is a record that represents a copper ore sample.
     */

    public record CopperOre(long sampleId) implements Ore {

        private static CopperOre random() {
            return new CopperOre(ThreadLocalRandom.current().nextLong(1000000));
        }
    }

    /**
     * IronBar is a record that represents an iron bar.
     */
    public record IronBar(long id, int size) {

        public IronBar {
            if (size < 1 || size > 10) {
                throw new IllegalArgumentException("Size must be between 1 and 10");
            }
        }

        static IronBar random(int size) {
            return new IronBar(ThreadLocalRandom.current().nextLong(1000000), size);
        }
    }

    /**
     * CopperBar is a record that represents a copper bar.
     */
    public record CopperBar(long id, int size) {

        public CopperBar {
            if (size < 1 || size > 10) {
                throw new IllegalArgumentException("Size must be between 1 and 10");
            }
        }

        static CopperBar random(int size) {
            return new CopperBar(ThreadLocalRandom.current().nextLong(1000000), size);
        }
    }

    /**
     * Cog is a record that represents a cog of a given size between 1 and 10
     * (inclusive).
     */
    public record Cog(long id, int size) {

        public Cog {
            if (size < 1 || size > 10) {
                throw new IllegalArgumentException("Size must be between 1 and 10");
            }
        }

        private static Cog random(int size) {
            return new Cog(ThreadLocalRandom.current().nextLong(1000000), size);
        }
    }

    /**
     * Forge a cog of a given size n a iron bar of size n and a copper bar of size
     * n.
     * 
     * @param size         the size of the cog to forge
     * @param ironBar   a iron bar of size n
     * @param copperBar a copper bar of size n
     * @return the forged cog
     * @throws InterruptedException if the thread is interrupted
     */
    static Cog forgeCog(int size, IronBar ironBar, CopperBar copperBar) throws InterruptedException {
        if (size < 1 || size > 10) {
            throw new IllegalArgumentException("Size must be between 1 and 10");
        }
        Objects.requireNonNull(ironBar);
        Objects.requireNonNull(copperBar);
        if (ironBar.size() != size) {
            throw new IllegalArgumentException("Needs " + size + " iron bar of size " + size + " to forge a cog of size " + size
                    + ", got " + ironBar.size());
        }
        if (copperBar.size() != size) {
            throw new IllegalArgumentException("Needs " + size + " copper bar of size " + size + " to forge a cog of size "
                    + size + ", got " + copperBar.size());
        }
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000 * size));
        return Cog.random(size);
    }

    /**
     * Mine an iron ore.
     * 
     * @return the mined iron ore
     * @throws InterruptedException if the thread is interrupted
     */

    static IronOre mineIron() throws InterruptedException {
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
        return IronOre.random();
    }

    /**
     * Mine a copper ore.
     * 
     * @return the mined copper ore
     * @throws InterruptedException if the thread is interrupted
     */
    static CopperOre mineCopper() throws InterruptedException {
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
        return CopperOre.random();
    }

    /**
     * Smelt a copper ore and produce a list of CopperBars.
     * 
     * @param ore the copper ore to smelt
     * @return a copper bar
     * @throws InterruptedException if the thread is interrupted
     */
    static CopperBar smeltCopperOre(CopperOre ore) throws InterruptedException {
        Objects.requireNonNull(ore);
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
        var barSize = (int) (1 + ore.sampleId() % 10);
        return CopperBar.random(barSize);
    }

    /**
     * Smelt an iron ore and produce a list of IronBars.
     * 
     * @param ore the iron ore to smelt
     * @return an iron bar
     * @throws InterruptedException if the thread is interrupted
     */
    static IronBar smeltIronOre(IronOre ore) throws InterruptedException {
        Objects.requireNonNull(ore);
        Thread.sleep(ThreadLocalRandom.current().nextLong(1000));
        var nbBars = (int) (1 + ore.sampleId() % 10);
        return IronBar.random(nbBars);
    }

    public static void main(String[] args) throws InterruptedException {
        var size = 2;
        IronBar ironBar = null;
        CopperBar copperBar = null;
        for (;;) {
            var ironOre = mineIron();
            ironBar = smeltIronOre(ironOre);
            if (ironBar.size() == size) {
                break;
            }
        }
        for (;;) {
            var copperOre = mineCopper();
            copperBar = smeltCopperOre(copperOre);
            if (copperBar.size() == size) {
                break;
            }
        }
        var cog = forgeCog(size, ironBar, copperBar);
        System.out.println(cog);
    }
}
