from __future__ import annotations

import base64

from pygitweb.static_tests.js_runtime import JsPage
from pygitweb.static_tests.pages import requested

_B64 = base64.b64encode(b"# Demo\n").decode("ascii")
_STATE = {
    "commit_href": "/commit",
    "display": "abc",
    "browse_href": "/tree",
    "log_href": "/log",
    "shortlog_href": "/shortlog",
    "tree_html": "<span class='tree'>files</span>",
    "readme_html": "<p>hi</p>",
}


def _page(*, project: str = "demo", full: bool = True) -> str:
    links = (
        '<a id="summary-ref-log-link"></a><a id="summary-ref-shortlog-link"></a>'
        '<div id="summary-tree-container"></div><a id="summary-tree-browse-link"></a>'
        if full
        else ""
    )
    return (
        '<select id="summary-ref-select"><option value="HEAD">HEAD</option>'
        '<option value="old" selected>old</option></select>'
        '<a id="summary-ref-commit-link"></a><a id="summary-ref-browse-link"></a>'
        + links
        + f'<div id="summary-readme-container" data-project="{project}"></div>'
        + f'<div class="page"></div><div id="readme-container" data-readme-b64="{_B64}"></div>'
    )


def test_missing_controls_skip_startup(js: JsPage) -> None:
    js.set_html("<div></div>")
    js.load("summary-ref-switcher.js")
    assert requested(js, "summary-refs") is False


def test_missing_project_skips_startup(js: JsPage) -> None:
    js.set_html(_page(project=""))
    js.load("summary-ref-switcher.js")
    assert requested(js, "summary-refs") is False


def test_options_replace_stale_entries_and_keep_a_live_value(js: JsPage) -> None:
    js.route("summary-refs", body={"options": [{"value": "old", "kind": "branch", "label": "old"}]})
    js.route("summary-ref-state", body=_STATE)
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    assert js.count("#summary-ref-select option") == 2
    assert js.prop("#summary-ref-select", "value") == "old"
    assert js.text("#summary-ref-select option:last-child") == "branch: old"


def test_a_removed_value_falls_back_to_head(js: JsPage) -> None:
    js.route("summary-refs", body={"options": [{"value": "main", "kind": "branch", "label": "main"}]})
    js.route("summary-ref-state", body=_STATE)
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    assert js.prop("#summary-ref-select", "value") == "HEAD"
    assert js.text("#summary-ref-select option:last-child") == "branch: main"


def test_failed_option_and_state_loads_leave_the_page_alone(js: JsPage) -> None:
    js.route("summary-refs", status=500)
    js.route("summary-ref-state", status=404)
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    js.set_prop("#summary-ref-select", "value", "HEAD")
    js.dispatch("#summary-ref-select", "change")
    assert js.text("#summary-ref-commit-link") == ""
    assert js.count("#summary-ref-select option") == 2


def test_change_loads_the_selected_ref(js: JsPage) -> None:
    js.route("summary-refs", body={"options": []})
    js.route("summary-ref-state", body=_STATE)
    js.set_html(_page(project="group/demo"))
    js.load("summary-ref-switcher.js")
    js.run(
        "document.getElementById('summary-ref-select').innerHTML = '<option value=\"feature/a\">feature/a</option>';"
    )
    js.dispatch("#summary-ref-select", "change")
    assert any("ref=feature%2Fa" in str(item["url"]) for item in js.requests())
    assert any("/project/group/demo/" in str(item["url"]) for item in js.requests())


def test_optional_links_are_skipped_when_absent(js: JsPage) -> None:
    js.route("summary-refs", body={"options": []})
    js.route("summary-ref-state", body=_STATE)
    js.set_html(_page(full=False))
    js.load("summary-ref-switcher.js")
    js.dispatch("#summary-ref-select", "change")
    assert js.text("#summary-ref-commit-link") == "abc"


def test_an_update_with_text_reloads_options_and_state(js: JsPage) -> None:
    js.route("summary-refs", body={"options": [{"value": "main", "kind": "branch", "label": "main"}]})
    js.route("summary-ref-state", body=_STATE)
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    before = len(js.requests())
    js.fulfill("updates=true", status=200, text="changed")
    assert len(js.requests()) > before
    assert js.text("#summary-ref-commit-link") == "abc"


def test_an_empty_update_does_not_reload(js: JsPage) -> None:
    js.route("summary-refs", body={"options": []})
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    refs = [item for item in js.requests() if "summary-refs" in str(item["url"])]
    js.fulfill("updates=true", status=200, text="")
    refs_after = [item for item in js.requests() if "summary-refs" in str(item["url"])]
    assert len(refs_after) == len(refs)


def test_not_modified_polls_again_without_a_backoff(js: JsPage) -> None:
    js.route("summary-refs", body={"options": []})
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    js.fulfill("updates=true", status=304, text="")
    updates = [item for item in js.requests() if "updates=true" in str(item["url"])]
    assert len(updates) >= 2
    assert js.value("__timers.length") == 0


def test_a_failed_update_backs_off_then_continues(js: JsPage) -> None:
    js.route("summary-refs", body={"options": []})
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    js.fulfill("updates=true", status=500, text="nope")
    assert js.value("__timers.length") == 1
    before = len([item for item in js.requests() if "updates=true" in str(item["url"])])
    js.tick()
    after = len([item for item in js.requests() if "updates=true" in str(item["url"])])
    assert after > before


def test_beforeunload_stops_the_subscription(js: JsPage) -> None:
    js.route("summary-refs", body={"options": []})
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    js.dispatch_window("beforeunload")
    assert js.value("__alerts") == []


def test_a_rejected_update_backs_off_until_abort(js: JsPage) -> None:
    js.route("updates=true", error="offline")
    js.route("summary-refs", body={"options": []})
    js.set_html(_page())
    js.load("summary-ref-switcher.js")
    assert js.value("__timers.length") >= 1
    js.dispatch_window("beforeunload")
    js.tick()
    assert js.text("#summary-ref-commit-link") == ""