from __future__ import annotations

import json

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

_ROOT = (
    '<div id="root"><p>see #abcd and #abc and @main and @1234 and @missing and (#eeee)</p>'
    "<pre><code>skip #abcd</code></pre><a href='/z'>keep @main</a></div>"
    '<textarea id="note"></textarea>'
)


def _load(js: JsPage) -> None:
    js.set_html(_ROOT)
    js.run("__manifest = " + json.dumps(MANIFEST))
    js.load("board-refs.js")


def test_linkify_resolves_commits_refs_and_skips_code(js: JsPage) -> None:
    _load(js)
    js.run("__loaded['board-refs.js'].linkifyElement(null, __manifest, 'demo')")
    js.run("__loaded['board-refs.js'].linkifyElement(document.getElementById('root'), __manifest, '')")
    assert js.count(".board-ref-link") == 0
    js.run("__loaded['board-refs.js'].linkifyElement(document.getElementById('root'), __manifest, 'group/demo')")
    assert js.attr(".board-ref-commit", "href") == "/c/abcd"
    assert js.attr(".board-ref-commit", "title") == "subject"
    assert js.count(".board-ref-commit") >= 2
    assert js.attr(".board-ref-tag", "href") == "/r/main"
    assert js.text(".board-ref-unresolved") == "@missing"
    assert js.attr(".board-ref-unresolved", "title") == "Unresolved reference"
    assert "#abcd" in js.text("pre")
    assert js.count("a[href='/z'] .board-ref-link") == 0


def test_unresolved_commit_links_to_the_project(js: JsPage) -> None:
    js.set_html("<div id='root'>#ffff</div>")
    js.run("__manifest = " + json.dumps(MANIFEST))
    js.load("board-refs.js")
    js.run("__loaded['board-refs.js'].linkifyElement(document.getElementById('root'), __manifest, 'group/demo')")
    href = js.attr(".board-ref-commit", "href") or ""
    assert "/project/group/demo" in href
    assert "h=ffff" in href
    assert js.attr(".board-ref-commit", "title") is None


def test_load_manifest_normalizes_payloads(js: JsPage) -> None:
    js.load("board-refs.js")
    js.route("board-ref-manifest", body=MANIFEST)
    got = js.settle("__loaded['board-refs.js'].loadManifest('group/demo', 'Sprint')")
    assert got["commits"][0]["insert"] == "abcd"
    assert any("b=Sprint" in str(item["url"]) and "/project/group/demo/" in str(item["url"]) for item in js.requests())
    js.route("board-ref-manifest", status=500, body={"commits": "nope", "refs": None})
    assert js.settle("__loaded['board-refs.js'].loadManifest('demo', '')") == {"commits": [], "refs": []}
    js.route("board-ref-manifest", body={"commits": "nope", "refs": {}})
    normalized = js.settle("__loaded['board-refs.js'].loadManifest('demo', 'Tasks')")
    assert normalized == {"commits": [], "refs": []}
    js.route("board-ref-manifest", error="offline")
    assert js.settle("__loaded['board-refs.js'].loadManifest('demo', 'Tasks')") == {"commits": [], "refs": []}


def test_autocomplete_inserts_a_suggestion(js: JsPage) -> None:
    _load(js)
    js.run("__loaded['board-refs.js'].attachAutocomplete(null, __manifest)")
    js.run("__loaded['board-refs.js'].attachAutocomplete(document.getElementById('note'), __manifest)")
    js.run("__loaded['board-refs.js'].attachAutocomplete(document.getElementById('note'), __manifest)")
    js.set_prop("#note", "value", "see @ma")
    js.set_prop("#note", "selectionStart", 7)
    js.dispatch("#note", "input")
    assert "d-none" not in js.classes(".board-ref-suggest")
    assert js.text(".board-ref-suggest-title") == "main"
    js.dispatch(".board-ref-suggest-item", "mousedown")
    assert js.prop("#note", "value") == "see @main"
    assert "d-none" in js.classes(".board-ref-suggest")


def test_autocomplete_filters_commits_and_caps_the_list(js: JsPage) -> None:
    commits = [
        {"insert": f"{index:04x}", "name": f"name-{index}", "url": "/c", "tags": "t", "snippet": "s"}
        for index in range(14)
    ]
    js.set_html("<textarea id='note'></textarea>")
    js.run("__manifest = " + json.dumps({"commits": commits, "refs": MANIFEST["refs"]}))
    js.load("board-refs.js")
    js.run("__loaded['board-refs.js'].attachAutocomplete(document.getElementById('note'), __manifest)")
    js.set_prop("#note", "value", "#")
    js.set_prop("#note", "selectionStart", 1)
    js.dispatch("#note", "keyup")
    assert js.count(".board-ref-suggest-item") == 12
    js.set_prop("#note", "value", "#1")
    js.set_prop("#note", "selectionStart", 2)
    js.dispatch("#note", "input")
    assert js.count(".board-ref-suggest-item") >= 1
    js.dispatch("#note", "keydown", key="Escape")
    assert "d-none" in js.classes(".board-ref-suggest")


def test_autocomplete_hides_on_blur_and_outside_click(js: JsPage) -> None:
    _load(js)
    js.run("__loaded['board-refs.js'].attachAutocomplete(document.getElementById('note'), __manifest)")
    js.set_prop("#note", "value", "@")
    js.set_prop("#note", "selectionStart", 1)
    js.dispatch("#note", "input")
    assert js.count(".board-ref-suggest-item") == 2
    js.dispatch("#note", "blur")
    assert "d-none" not in js.classes(".board-ref-suggest")
    js.tick()
    assert "d-none" in js.classes(".board-ref-suggest")
    js.set_prop("#note", "value", "@ma")
    js.set_prop("#note", "selectionStart", 3)
    js.dispatch("#note", "input")
    js.dispatch("#root", "mousedown")
    assert "d-none" in js.classes(".board-ref-suggest")


def test_autocomplete_ignores_text_without_a_token(js: JsPage) -> None:
    _load(js)
    js.run("__loaded['board-refs.js'].attachAutocomplete(document.getElementById('note'), __manifest)")
    js.set_prop("#note", "value", "hello")
    js.set_prop("#note", "selectionStart", 5)
    js.dispatch("#note", "input")
    assert js.count(".board-ref-suggest-item") == 0


def test_suggestion_dates_and_snippets_are_escaped(js: JsPage) -> None:
    manifest = {
        "commits": [],
        "refs": [{"insert": "main", "name": "<b>", "url": "/r", "tags": "main", "snippet": "<i>", "created": "<d>"}],
    }
    js.set_html("<textarea id='note'></textarea>")
    js.run("__manifest = " + json.dumps(manifest))
    js.load("board-refs.js")
    js.run("__loaded['board-refs.js'].attachAutocomplete(document.getElementById('note'), __manifest)")
    js.set_prop("#note", "value", "@")
    js.set_prop("#note", "selectionStart", 1)
    js.dispatch("#note", "input")
    html = js.prop(".board-ref-suggest-item", "innerHTML")
    assert "&lt;b&gt;" in html
    assert "&lt;i&gt;" in html
    assert "&lt;d&gt;" in html
    assert "<b>" not in html