from microdot import Microdot, send_file
from microdot.session import with_session
from models.zone import Zone
from models.profile import Profile
import json

controller = Microdot()
zones = Zone()
profiles = Profile()


def get_current_profile_id(session=None):
    """Get the current active profile ID from session or fallback to first."""
    profile_list = profiles.list()
    session_profile = None
    if session is not None:
        session_profile = session.get("current_profile")
    if session_profile and session_profile in profile_list:
        return session_profile
    if profile_list:
        return profile_list[0]
    return None


def _profile_zone_id_list(profile):
    """Ordered zone ids for a profile (``zones``, legacy ``tabs``, or ``zone_order``)."""
    if not profile or not isinstance(profile, dict):
        return []
    z = profile.get("zones")
    if isinstance(z, list) and z:
        return list(z)
    t = profile.get("zones")
    if isinstance(t, list) and t:
        return list(t)
    o = profile.get("zone_order")
    if isinstance(o, list) and o:
        return list(o)
    return []


def get_profile_zone_order(profile_id):
    if not profile_id:
        return []
    profile = profiles.read(profile_id)
    return _profile_zone_id_list(profile)


def _set_profile_zone_order(profile, ids):
    profile["zones"] = list(ids)
    profile.pop("tabs", None)
    profile.pop("zone_order", None)


def get_current_zone_id(request, session=None):
    """Cookie ``current_zone``, legacy ``current_zone``, then first zone in profile."""
    z = request.cookies.get("current_zone") or request.cookies.get("current_zone")
    if z:
        return z
    profile_id = get_current_profile_id(session)
    if profile_id:
        profile = profiles.read(profile_id)
        order = _profile_zone_id_list(profile)
        if order:
            return order[0]
    return None


def _render_zones_list_fragment(request, session):
    """Render zone strip HTML for HTMX / JS."""
    profile_id = get_current_profile_id(session)
    if not profile_id:
        return (
            '<div class="zones-list">No profile selected</div>',
            200,
            {"Content-Type": "text/html"},
        )

    zone_order = get_profile_zone_order(profile_id)
    current_zone_id = get_current_zone_id(request, session)

    html = '<div class="zones-list">'
    for zid in zone_order:
        zdata = zones.read(zid)
        if zdata:
            active_class = "active" if str(zid) == str(current_zone_id) else ""
            zname = zdata.get("name", "Zone " + str(zid))
            html += (
                '<button class="zone-button ' + active_class + '" '
                'hx-get="/zones/' + str(zid) + '/content-fragment" '
                'hx-target="#zone-content" '
                'hx-swap="innerHTML" '
                'hx-push-url="true" '
                'hx-trigger="click" '
                'onclick="document.querySelectorAll(\'.zone-button\').forEach(b => b.classList.remove(\'active\')); this.classList.add(\'active\');">'
                + zname
                + "</button>"
            )
    html += "</div>"
    return html, 200, {"Content-Type": "text/html"}


def _render_zone_content_fragment(request, session, id):
    if id == "current":
        current_zone_id = get_current_zone_id(request, session)
        if not current_zone_id:
            accept_header = request.headers.get("Accept", "")
            wants_html = "text/html" in accept_header
            if wants_html:
                return (
                    '<div class="error">No current zone set</div>',
                    404,
                    {"Content-Type": "text/html"},
                )
            return json.dumps({"error": "No current zone set"}), 404
        id = current_zone_id

    z = zones.read(id)
    if not z:
        return '<div>Zone not found</div>', 404, {"Content-Type": "text/html"}

    session["current_zone"] = str(id)
    session.save()

    if not request.headers.get("HX-Request"):
        return send_file("templates/index.html")

    html = (
        '<div class="presets-section" data-zone-id="' + str(id) + '">'
        "<h3>Presets</h3>"
        '<div class="profiles-actions" style="margin-bottom: 1rem;"></div>'
        '<div id="presets-list-zone" class="presets-list">'
        "<!-- Presets will be loaded here -->"
        "</div>"
        "</div>"
    )
    return html, 200, {"Content-Type": "text/html"}


@controller.get("/<id>/content-fragment")
@with_session
async def zone_content_fragment(request, session, id):
    return _render_zone_content_fragment(request, session, id)


@controller.get("")
@with_session
async def list_zones(request, session):
    profile_id = get_current_profile_id(session)
    current_zone_id = get_current_zone_id(request, session)
    zone_order = get_profile_zone_order(profile_id) if profile_id else []

    zones_data = {}
    for zid in zones.list():
        zdata = zones.read(zid)
        if zdata:
            zones_data[zid] = zdata

    return (
        json.dumps(
            {
                "zones": zones_data,
                "zone_order": zone_order,
                "current_zone_id": current_zone_id,
                "profile_id": profile_id,
            }
        ),
        200,
        {"Content-Type": "application/json"},
    )


@controller.get("/current")
@with_session
async def get_current_zone(request, session):
    current_zone_id = get_current_zone_id(request, session)
    if not current_zone_id:
        return (
            json.dumps({"error": "No current zone set", "zone": None, "zone_id": None}),
            404,
        )

    z = zones.read(current_zone_id)
    if z:
        return (
            json.dumps({"zone": z, "zone_id": current_zone_id}),
            200,
            {"Content-Type": "application/json"},
        )
    return (
        json.dumps({"error": "Zone not found", "zone": None, "zone_id": None}),
        404,
    )


@controller.post("/<id>/set-current")
async def set_current_zone(request, id):
    z = zones.read(id)
    if not z:
        return json.dumps({"error": "Zone not found"}), 404

    response_data = json.dumps({"message": "Current zone set", "zone_id": id})
    return (
        response_data,
        200,
        {
            "Content-Type": "application/json",
            "Set-Cookie": (
                f"current_zone={id}; Path=/; Max-Age=31536000; SameSite=Lax"
            ),
        },
    )


@controller.get("/<id>")
async def get_zone(request, id):
    z = zones.read(id)
    if z:
        return json.dumps(z), 200, {"Content-Type": "application/json"}
    return json.dumps({"error": "Zone not found"}), 404


@controller.put("/<id>")
async def update_zone(request, id):
    try:
        data = request.json
        if zones.update(id, data):
            return json.dumps(zones.read(id)), 200, {"Content-Type": "application/json"}
        return json.dumps({"error": "Zone not found"}), 404
    except Exception as e:
        return json.dumps({"error": str(e)}), 400


@controller.delete("/<id>")
@with_session
async def delete_zone(request, session, id):
    try:
        if id == "current":
            current_zone_id = get_current_zone_id(request, session)
            if current_zone_id:
                id = current_zone_id
            else:
                return json.dumps({"error": "No current zone to delete"}), 404

        if zones.delete(id):
            profile_id = get_current_profile_id(session)
            if profile_id:
                profile = profiles.read(profile_id)
                if profile:
                    zlist = _profile_zone_id_list(profile)
                    if id in zlist:
                        zlist.remove(id)
                        _set_profile_zone_order(profile, zlist)
                        profiles.update(profile_id, profile)

            current_zone_id = get_current_zone_id(request, session)
            if current_zone_id == id:
                response_data = json.dumps({"message": "Zone deleted successfully"})
                return (
                    response_data,
                    200,
                    {
                        "Content-Type": "application/json",
                        "Set-Cookie": (
                            "current_zone=; Path=/; Max-Age=0; SameSite=Lax"
                        ),
                    },
                )

            return json.dumps({"message": "Zone deleted successfully"}), 200, {
                "Content-Type": "application/json"
            }

        return json.dumps({"error": "Zone not found"}), 404
    except Exception as e:
        import sys

        try:
            sys.print_exception(e)
        except Exception:
            pass
        return json.dumps({"error": str(e)}), 500, {"Content-Type": "application/json"}


@controller.post("")
@with_session
async def create_zone(request, session):
    try:
        if request.form:
            name = request.form.get("name", "").strip()
            ids_str = request.form.get("ids", "1").strip()
            names = [i.strip() for i in ids_str.split(",") if i.strip()]
            preset_ids = None
        else:
            data = request.json or {}
            name = data.get("name", "")
            names = data.get("names")
            if names is None:
                names = data.get("ids")
            preset_ids = data.get("presets", None)

        if not name:
            return json.dumps({"error": "Zone name cannot be empty"}), 400

        zid = zones.create(name, names, preset_ids)

        profile_id = get_current_profile_id(session)
        if profile_id:
            profile = profiles.read(profile_id)
            if profile:
                zlist = _profile_zone_id_list(profile)
                if zid not in zlist:
                    zlist.append(zid)
                    _set_profile_zone_order(profile, zlist)
                    profiles.update(profile_id, profile)

        zdata = zones.read(zid)
        return json.dumps({zid: zdata}), 201, {"Content-Type": "application/json"}
    except Exception as e:
        import sys

        sys.print_exception(e)
        return json.dumps({"error": str(e)}), 400


@controller.post("/<id>/clone")
@with_session
async def clone_zone(request, session, id):
    try:
        source = zones.read(id)
        if not source:
            return json.dumps({"error": "Zone not found"}), 404

        data = request.json or {}
        source_name = source.get("name") or f"Zone {id}"
        new_name = data.get("name") or f"{source_name} Copy"
        clone_id = zones.create(new_name, source.get("names"), source.get("presets"))
        extra = {k: v for k, v in source.items() if k not in ("name", "names", "presets")}
        if extra:
            zones.update(clone_id, extra)

        profile_id = get_current_profile_id(session)
        if profile_id:
            profile = profiles.read(profile_id)
            if profile:
                zlist = _profile_zone_id_list(profile)
                if clone_id not in zlist:
                    zlist.append(clone_id)
                    _set_profile_zone_order(profile, zlist)
                    profiles.update(profile_id, profile)

        zdata = zones.read(clone_id)
        return json.dumps({clone_id: zdata}), 201, {"Content-Type": "application/json"}
    except Exception as e:
        import sys

        try:
            sys.print_exception(e)
        except Exception:
            pass
        return json.dumps({"error": str(e)}), 400

