package thb.jeanluc.adventure.io.text;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Renders a {@link MapView} as a styled ASCII grid: boxed rooms connected by
 * corridor lines. Box-drawing uses Unicode or pure ASCII depending on {@code unicode}.
 */
public final class AsciiMap {

    private static final int MIN_INNER = 7;   // minimum room name field width
    private static final int MAX_INNER = 14;  // cap so the map stays compact
    private static final int GAP = 3;         // horizontal corridor width

    private AsciiMap() {
    }

    public static StyledText render(MapView view, boolean unicode) {
        if (view.cells().isEmpty()) {
            return StyledText.of("(no map yet)");
        }

        int maxX = 0;
        int maxY = 0;
        int inner = MIN_INNER;
        Map<Long, RoomCell> at = new HashMap<>();
        for (RoomCell c : view.cells()) {
            maxX = Math.max(maxX, c.x());
            maxY = Math.max(maxY, c.y());
            inner = Math.max(inner, Math.min(MAX_INNER, c.name().length()));
            at.put(key(c.x(), c.y()), c);
        }

        Set<Long> hEdge = new HashSet<>();
        Set<Long> vEdge = new HashSet<>();
        for (Connection cn : view.connections()) {
            RoomCell a = cn.from();
            RoomCell b = cn.to();
            if (a.y() == b.y()) {
                hEdge.add(key(Math.min(a.x(), b.x()), a.y()));
            } else if (a.x() == b.x()) {
                vEdge.add(key(a.x(), Math.min(a.y(), b.y())));
            }
        }

        String tl = unicode ? "┌" : "+";
        String tr = unicode ? "┐" : "+";
        String bl = unicode ? "└" : "+";
        String br = unicode ? "┘" : "+";
        String hz = unicode ? "─" : "-";
        String vt = unicode ? "│" : "|";

        int boxW = inner + 2;
        int leftPad = (boxW - 1) / 2;
        int rightPad = boxW - 1 - leftPad;

        StyledText.Builder b = StyledText.builder();
        for (int gy = 0; gy <= maxY; gy++) {
            for (int line = 0; line < 3; line++) {
                for (int gx = 0; gx <= maxX; gx++) {
                    RoomCell c = at.get(key(gx, gy));
                    if (c == null) {
                        b.plain(" ".repeat(boxW));
                    } else if (line == 0) {
                        styled(b, tl + hz.repeat(inner) + tr, c.state());
                    } else if (line == 2) {
                        styled(b, bl + hz.repeat(inner) + br, c.state());
                    } else {
                        styled(b, vt + center(label(c, inner), inner) + vt, c.state());
                    }
                    if (gx < maxX) {
                        if (line == 1 && hEdge.contains(key(gx, gy))) {
                            b.dim(hz.repeat(GAP));
                        } else {
                            b.plain(" ".repeat(GAP));
                        }
                    }
                }
                b.plain("\n");
            }
            if (gy < maxY) {
                for (int gx = 0; gx <= maxX; gx++) {
                    if (vEdge.contains(key(gx, gy))) {
                        b.plain(" ".repeat(leftPad)).dim(vt).plain(" ".repeat(rightPad));
                    } else {
                        b.plain(" ".repeat(boxW));
                    }
                    if (gx < maxX) {
                        b.plain(" ".repeat(GAP));
                    }
                }
                b.plain("\n");
            }
        }
        return b.build();
    }

    private static void styled(StyledText.Builder b, String s, CellState state) {
        switch (state) {
            case CURRENT -> b.heading(s);
            case KNOWN -> b.dim(s);
            default -> b.plain(s);
        }
    }

    private static String label(RoomCell c, int inner) {
        String n = c.name();
        if (n.length() > inner) {
            n = n.substring(0, inner);
        }
        return n;
    }

    private static String center(String s, int width) {
        int total = width - s.length();
        int left = total / 2;
        int right = total - left;
        return " ".repeat(left) + s + " ".repeat(right);
    }

    private static long key(int x, int y) {
        return (((long) x) << 32) ^ (y & 0xffffffffL);
    }
}
