package cn.yunzhixue.ability.center.examsprint.infrastructure.report.pdf;

import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.GraphicsEnvironment;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

public final class BundledOutlookReportFonts {

    private static final String DEFAULT_RESOURCE_PATH = "/fonts/MiSans-VF.ttf";
    private static final String TEMP_FILE_NAME = "MiSans-VF.ttf";
    private static final String AWT_FONT_PROBE_TEXT = "掌握率";
    private static final Map<String, BundledOutlookReportFonts> CACHE = new HashMap<>();

    private final List<Registration> registrations;

    private BundledOutlookReportFonts(List<Registration> registrations) {
        this.registrations = List.copyOf(registrations);
    }

    public static synchronized BundledOutlookReportFonts load() {
        return load(DEFAULT_RESOURCE_PATH);
    }

    public static synchronized BundledOutlookReportFonts load(String resourcePath) {
        Objects.requireNonNull(resourcePath, "resourcePath");

        BundledOutlookReportFonts cached = CACHE.get(resourcePath);
        if (cached != null && cached.isHealthy()) {
            return cached;
        }

        File copiedFont = copyToTempFile(resourcePath);
        registerWithAwt(resourcePath, copiedFont);

        BundledOutlookReportFonts loaded = new BundledOutlookReportFonts(List.of(new Registration("MiSans", copiedFont)));
        CACHE.put(resourcePath, loaded);
        return loaded;
    }

    public List<Registration> registrations() {
        return registrations;
    }

    private boolean isHealthy() {
        return registrations.size() == 1 && isHealthy(registrations.get(0).file());
    }

    private static boolean isHealthy(File file) {
        return file.exists() && file.isFile() && file.canRead() && file.length() > 0L;
    }

    private static File copyToTempFile(String resourcePath) {
        try (InputStream inputStream = BundledOutlookReportFonts.class.getResourceAsStream(resourcePath)) {
            if (inputStream == null) {
                throw new BundledFontMissingException("Bundled font resource not found on classpath: " + resourcePath);
            }

            Path tempDirectory = Files.createTempDirectory("bundled-outlook-report-font-");
            Path tempFile = tempDirectory.resolve(TEMP_FILE_NAME);
            Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
            File copiedFile = tempFile.toFile();
            tempDirectory.toFile().deleteOnExit();
            copiedFile.deleteOnExit();
            if (!isHealthy(copiedFile)) {
                throw new BundledFontUnavailableException("Bundled font copy is not readable: " + resourcePath + " -> " + tempFile);
            }
            return copiedFile;
        } catch (IOException exception) {
            throw new BundledFontUnavailableException("Failed to copy bundled font resource: " + resourcePath, exception);
        }
    }

    private static void registerWithAwt(String resourcePath, File fontFile) {
        try {
            Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
            String family = font.getFamily(Locale.ROOT);
            boolean registered = GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
            if (!registered && !isAwtFontUsable(family)) {
                throw new BundledFontUnavailableException(
                        "Failed to register bundled font with Java AWT: " + resourcePath + " -> " + fontFile);
            }
            if (!isAwtFontUsable(family)) {
                throw new BundledFontUnavailableException(
                        "Bundled font registered with Java AWT but cannot display report CJK text: "
                                + resourcePath + " -> " + fontFile + " (family: " + family + ")");
            }
        } catch (FontFormatException exception) {
            throw new BundledFontUnavailableException(
                    "Bundled font resource is not a valid TrueType font for Java AWT: " + resourcePath + " -> " + fontFile,
                    exception);
        } catch (IOException exception) {
            throw new BundledFontUnavailableException(
                    "Failed to load bundled font into Java AWT: " + resourcePath + " -> " + fontFile,
                    exception);
        }
    }

    private static boolean isAwtFontUsable(String family) {
        return family != null && !family.isBlank()
                && new Font(family, Font.PLAIN, 12).canDisplayUpTo(AWT_FONT_PROBE_TEXT) == -1;
    }

    static class BundledFontUnavailableException extends RuntimeException {

        BundledFontUnavailableException(String message) {
            super(message);
        }

        BundledFontUnavailableException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    static final class BundledFontMissingException extends BundledFontUnavailableException {

        BundledFontMissingException(String message) {
            super(message);
        }
    }

    public record Registration(String family, File file) {
        public Registration {
            Objects.requireNonNull(family, "family");
            Objects.requireNonNull(file, "file");
        }
    }
}
