package com.cameleer.license.minter.cli;

import com.cameleer.license.minter.LicenseMinter;
import com.cameleer.server.core.license.LicenseInfo;

import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.Base64;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;

public final class LicenseMinterCli {

    private static final Set<String> KNOWN_FLAGS = Set.of(
            "--private-key", "--public-key", "--tenant", "--label",
            "--expires", "--grace-days", "--output", "--verify"
    );

    public static void main(String[] args) {
        System.exit(run(args));
    }

    public static int run(String[] args) {
        return run(args, System.out, System.err);
    }

    public static int run(String[] args, PrintStream out, PrintStream err) {
        Map<String, String> flags = new LinkedHashMap<>();
        Set<String> bool = new HashSet<>();
        Map<String, Integer> limits = new TreeMap<>();
        for (String arg : args) {
            if (!arg.startsWith("--")) {
                err.println("unexpected positional argument: " + arg);
                return 2;
            }
            int eq = arg.indexOf('=');
            String key = eq < 0 ? arg : arg.substring(0, eq);
            String value = eq < 0 ? null : arg.substring(eq + 1);
            if (key.startsWith("--max-")) {
                String limitKey = "max_" + key.substring("--max-".length()).replace('-', '_');
                if (value == null) {
                    err.println("missing value for " + key);
                    return 2;
                }
                limits.put(limitKey, Integer.parseInt(value));
                continue;
            }
            if (!KNOWN_FLAGS.contains(key)) {
                err.println("unknown flag: " + key);
                return 2;
            }
            if (value == null) {
                bool.add(key);
            } else {
                flags.put(key, value);
            }
        }

        String privPath = flags.get("--private-key");
        String tenant = flags.get("--tenant");
        String expiresIso = flags.get("--expires");
        if (privPath == null || tenant == null || expiresIso == null) {
            err.println("required: --private-key --tenant --expires");
            return 2;
        }

        try {
            PrivateKey privateKey = readEd25519PrivateKey(Path.of(privPath));
            int graceDays = Integer.parseInt(flags.getOrDefault("--grace-days", "0"));
            Instant exp = LocalDate.parse(expiresIso).atStartOfDay(ZoneOffset.UTC).toInstant();
            LicenseInfo info = new LicenseInfo(
                    UUID.randomUUID(),
                    tenant,
                    flags.get("--label"),
                    Collections.unmodifiableMap(limits),
                    Instant.now(),
                    exp,
                    graceDays
            );
            String token = LicenseMinter.mint(info, privateKey);

            String outPath = flags.get("--output");
            if (outPath != null) {
                Files.writeString(Path.of(outPath), token);
                out.println("wrote " + outPath);
            } else {
                out.println(token);
            }
            if (bool.contains("--verify")) {
                String pubPath = flags.get("--public-key");
                if (pubPath == null) {
                    err.println("--verify requires --public-key");
                    if (outPath != null) Files.deleteIfExists(Path.of(outPath));
                    return 2;
                }
                try {
                    String pubB64 = Files.readString(Path.of(pubPath)).trim();
                    new com.cameleer.server.core.license.LicenseValidator(pubB64, tenant).validate(token);
                    out.println("verified ok");
                } catch (Exception ve) {
                    err.println("VERIFY FAILED: " + ve.getMessage());
                    if (outPath != null) Files.deleteIfExists(Path.of(outPath));
                    return 3;
                }
            }
            return 0;
        } catch (Exception e) {
            err.println("ERROR: " + e.getMessage());
            return 1;
        }
    }

    private static PrivateKey readEd25519PrivateKey(Path path) throws Exception {
        String s = Files.readString(path).trim();
        if (s.startsWith("-----BEGIN")) {
            s = s.replaceAll("-----BEGIN [A-Z ]+-----", "")
                 .replaceAll("-----END [A-Z ]+-----", "")
                 .replaceAll("\\s", "");
        }
        byte[] der = Base64.getDecoder().decode(s);
        return KeyFactory.getInstance("Ed25519")
                .generatePrivate(new PKCS8EncodedKeySpec(der));
    }
}
