from __future__ import annotations

import json
import os
import threading
from pathlib import Path

import pytest

if os.getenv("STACKCHAIN_RUN_RELEASE_E2E") != "1":
    pytest.skip("packaged Tomorrow conflict journey runs only in its gated CI job", allow_module_level=True)
pytest.importorskip("playwright.sync_api")
from playwright.sync_api import expect, sync_playwright

from fake_gitea import FakeGiteaServer
from test_mobile_offline_issue_release import ACCESS_TOKEN, ROOT, release_server


@pytest.mark.parametrize(("width", "height"), [(320, 568), (390, 844)])
def test_release_artifact_resolves_cross_device_tomorrow_conflicts_on_mobile(
    tmp_path: Path, width: int, height: int
):
    archives = sorted((ROOT / "dist").glob("stackchain-dashboard-*.tar.gz"))
    assert len(archives) == 1, "browser job must download exactly one assembled release archive"

    fake = FakeGiteaServer(("127.0.0.1", 0))
    fake_thread = threading.Thread(target=fake.serve_forever, daemon=True)
    fake_thread.start()
    fake_url = f"http://127.0.0.1:{fake.server_port}"
    put_bodies: list[dict] = []

    try:
        with release_server(archives[0], tmp_path, fake_url) as origin, sync_playwright() as playwright:
            browser = playwright.chromium.launch(args=["--ignore-certificate-errors"])
            context = browser.new_context(
                viewport={"width": width, "height": height}, ignore_https_errors=True
            )
            page = context.new_page()

            def tomorrow_route(route):
                if route.request.method == "PUT":
                    body = json.loads(route.request.post_data or "{}")
                    put_bodies.append(body)
                    if body.get("base_revision") != 12:
                        route.fulfill(status=409, content_type="application/json", body='{"detail":"changed elsewhere"}')
                        return
                    route.fulfill(
                        status=200,
                        content_type="application/json",
                        body=json.dumps({"revision": 13, **body}),
                    )
                    return
                route.fulfill(
                    status=200,
                    content_type="application/json",
                    body=json.dumps(
                        {
                            "revision": 12,
                            "ids": ["issue:acme/mobile:server:"],
                            "capacity_minutes": 60,
                            "estimates": {"issue:acme/mobile:server:": 30},
                            "plan_date": "2026-08-21",
                            "timezone": "UTC",
                        }
                    ),
                )

            page.route("**/api/v1/tomorrow", tomorrow_route)
            page.goto(origin + "/", wait_until="networkidle")
            page.locator('input[name="device_label"]').fill("Tomorrow conflict release phone")
            page.locator('input[name="access_token"]').fill(ACCESS_TOKEN)
            page.locator("#submit-sign-in").click()
            page.wait_for_url(origin + "/", wait_until="networkidle")

            def stage_phone_plan():
                page.evaluate(
                    """() => {
                      localStorage.setItem('stackchain.tomorrow-sync.v1.timmy', JSON.stringify({
                        base_revision:11, revision:11,
                        ids:['issue:acme/mobile:phone:'], capacity_minutes:90,
                        estimates:{'issue:acme/mobile:phone:':45},
                        plan_date:'2026-08-21', timezone:'UTC', sync_pending:true,
                      }));
                      window.dispatchEvent(new Event('online'));
                    }"""
                )
                expect(page.locator("#mobile-tomorrow-summary")).to_have_text(
                    "Conflict · review required"
                )

            def open_conflict():
                queue_sheet = page.locator("#mobile-queue-sheet")
                if not queue_sheet.evaluate("element => element.open"):
                    page.locator('[data-mobile-task="queues"]').click()
                page.locator('[data-mobile-queue="tomorrow"]').click()
                review = page.locator("#tomorrow-conflict-review")
                expect(review).to_be_visible()
                expect(page.locator("#plan-today-title")).to_have_text(
                    "Resolve Tomorrow conflict"
                )
                expect(page.locator("#tomorrow-conflict-phone-plan")).to_contain_text(
                    "issue:acme/mobile:phone:"
                )
                expect(page.locator("#tomorrow-conflict-phone-plan")).to_contain_text(
                    "45 min"
                )
                expect(page.locator("#tomorrow-conflict-server-plan")).to_contain_text(
                    "issue:acme/mobile:server:"
                )
                expect(page.locator("#tomorrow-conflict-server-plan")).to_contain_text(
                    "30 min"
                )
                expect(page.locator("#keep-phone-tomorrow")).to_be_focused()
                for selector in ("#keep-phone-tomorrow", "#use-server-tomorrow"):
                    bounds = page.locator(selector).bounding_box()
                    assert bounds and bounds["height"] >= 44
                assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")

            stage_phone_plan()
            open_conflict()
            initial_puts = len(put_bodies)
            page.locator("#use-server-tomorrow").click()
            expect(page.locator("#plan-today-sheet")).to_be_hidden()
            expect(page.locator("#mobile-tomorrow-summary")).to_contain_text("1 planned")
            assert len(put_bodies) == initial_puts

            stage_phone_plan()
            open_conflict()
            page.locator("#keep-phone-tomorrow").click()
            expect(page.locator("#plan-today-sheet")).to_be_hidden()
            expect(page.locator("#mobile-tomorrow-summary")).to_contain_text("1 planned · 45 of 90 min")
            assert put_bodies[-1]["base_revision"] == 12
            assert put_bodies[-1]["ids"] == ["issue:acme/mobile:phone:"]
            assert page.evaluate(
                "localStorage.getItem('stackchain.tomorrow-sync.v1.timmy')"
            ) is None
            assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
            browser.close()
    finally:
        fake.shutdown()
        fake.server_close()
        fake_thread.join(timeout=5)
