"""ORCH-067 — Group C: clickable issue number in the live card (AC-10/AC-11/AC-14).

The issue number in the card header is now a Plane hyperlink
(``<a href=".../issues/<id>/">ORCH-NNN</a>``) when a usable browser URL can be
built, and degrades fail-safe to the html-escaped raw number when any piece is
missing (web base / non-loopback / workspace / project_id / plane_issue_id). The
card must NEVER break under parse_mode=HTML: a title with '<'/'&'/'>' stays
escaped while the <a> markup stays valid. Network is isolated (no HTTP from the
render path here); the DB is a temp SQLite.

Test ids TC-10, TC-11, TC-16 from 04-test-plan.yaml.
"""

import os
import tempfile

os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")
os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")

_test_db = os.path.join(tempfile.gettempdir(), "test_orchestrator_card_link.db")
os.environ["ORCH_DB_PATH"] = _test_db

from types import SimpleNamespace  # noqa: E402

import pytest  # noqa: E402

import src.db as db_module  # noqa: E402
import src.projects as projects_mod  # noqa: E402
from src.db import init_db, get_db  # noqa: E402
from src import notifications as N  # noqa: E402

# orchestrator repo -> default project registry uuid (src/projects.py).
_ORCH_PROJECT_ID = "8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a"


@pytest.fixture(autouse=True)
def setup_db(monkeypatch):
    monkeypatch.setattr(db_module.settings, "db_path", _test_db, raising=False)
    if os.path.exists(_test_db):
        os.unlink(_test_db)
    init_db()
    # Keep the render path fully offline (no live overlay HTTP).
    monkeypatch.setattr(N._get_settings(), "tracker_live_status", False,
                        raising=False)
    # Pin the repo->project resolution so cross-file tests that reload the
    # ORCH_PROJECTS_JSON registry can't strip 'orchestrator' out from under us.
    monkeypatch.setattr(
        projects_mod, "get_project_by_repo",
        lambda repo: (SimpleNamespace(plane_project_id=_ORCH_PROJECT_ID)
                      if repo == "orchestrator" else None),
    )
    yield
    if os.path.exists(_test_db):
        os.unlink(_test_db)


def _set(monkeypatch, **kw):
    s = N._get_settings()
    for k, v in kw.items():
        monkeypatch.setattr(s, k, v, raising=False)


def _mk_task(wid="ORCH-067", repo="orchestrator", title="card link",
             plane_issue_id="issue-uuid-1", stage="development"):
    conn = get_db()
    cur = conn.execute(
        "INSERT INTO tasks (plane_id, work_item_id, repo, branch, stage, title, "
        "plane_issue_id) VALUES (?, ?, ?, ?, ?, ?, ?)",
        ("p1", wid, repo, "feature/ORCH-067-x", stage, title, plane_issue_id),
    )
    tid = cur.lastrowid
    conn.commit()
    conn.close()
    return tid


# --------------------------------------------------------------------------- #
# TC-10 / AC-10 — full data -> clickable <a> wrapping the issue number
# --------------------------------------------------------------------------- #
def test_tc10_card_number_is_clickable(monkeypatch):
    _set(monkeypatch, plane_web_url="https://plane.example.org",
         plane_api_url="http://localhost:8091", plane_workspace_slug="acme")
    tid = _mk_task(plane_issue_id="abcd-issue-uuid")

    text = N.render_task_tracker(tid)
    expected_url = (
        f"https://plane.example.org/acme/projects/{_ORCH_PROJECT_ID}"
        f"/issues/abcd-issue-uuid/"
    )
    assert f'<a href="{expected_url}">ORCH-067</a>' in text


# --------------------------------------------------------------------------- #
# TC-11 / AC-11 — fail-safe: any missing piece -> escaped number, no <a>, no crash
# --------------------------------------------------------------------------- #
@pytest.mark.parametrize("override,reason", [
    ({"plane_web_url": "", "plane_api_url": ""}, "no web base"),
    ({"plane_web_url": "http://localhost:8091", "plane_api_url": ""}, "loopback base"),
    ({"plane_workspace_slug": ""}, "no workspace"),
])
def test_tc11_card_number_degrades_settings(monkeypatch, override, reason):
    _set(monkeypatch, plane_web_url="https://plane.example.org",
         plane_api_url="http://localhost:8091", plane_workspace_slug="acme")
    _set(monkeypatch, **override)
    tid = _mk_task(plane_issue_id="abcd-issue-uuid")

    text = N.render_task_tracker(tid)
    assert "ORCH-067" in text                 # raw number still shown
    assert "<a href=" not in text, reason     # but NOT a link
    assert "localhost" not in text            # never leak a loopback URL


def test_tc11_card_number_degrades_no_issue_id(monkeypatch):
    # Missing plane_issue_id -> the number is shown unlinked, render survives.
    _set(monkeypatch, plane_web_url="https://plane.example.org",
         plane_workspace_slug="acme")
    tid = _mk_task(plane_issue_id=None)
    text = N.render_task_tracker(tid)
    assert "ORCH-067" in text
    assert "<a href=" not in text


def test_tc11_card_number_degrades_unknown_repo(monkeypatch):
    # repo not in the registry -> no project_id -> number unlinked, no crash.
    _set(monkeypatch, plane_web_url="https://plane.example.org",
         plane_workspace_slug="acme")
    tid = _mk_task(repo="not-a-real-repo", plane_issue_id="abcd-issue-uuid")
    text = N.render_task_tracker(tid)
    assert "ORCH-067" in text
    assert "<a href=" not in text


# --------------------------------------------------------------------------- #
# TC-16 / AC-14 — HTML escaping: title with '<b>'/'&'/'>' stays safe + valid <a>
# --------------------------------------------------------------------------- #
def test_tc16_title_escaped_link_valid(monkeypatch):
    _set(monkeypatch, plane_web_url="https://plane.example.org",
         plane_workspace_slug="acme")
    tid = _mk_task(title="<b>drop & </b> table >", plane_issue_id="iss-1")

    text = N.render_task_tracker(tid)
    # Raw title markup is escaped -> cannot break parse_mode=HTML.
    assert "<b>" not in text
    assert "&lt;b&gt;" in text
    assert "&amp;" in text
    # The card's own anchor markup stays well-formed (balanced tags).
    assert text.count("<a href=") == text.count("</a>")
    assert text.count("<a href=") >= 1          # the clickable number is present


def test_tc16_ampersand_in_work_item_id_escaped(monkeypatch):
    # A '&' in the work_item_id is escaped in the (unlinked) fail-safe path too.
    _set(monkeypatch, plane_web_url="", plane_api_url="",
         plane_workspace_slug="acme")
    tid = _mk_task(wid="ORCH&67", plane_issue_id="iss-1")
    text = N.render_task_tracker(tid)
    assert "ORCH&amp;67" in text
    assert "<a href=" not in text               # no link (no web base)
