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