# retoor <retoor@molodetz.nl>

import re
from playwright.sync_api import expect
from tests.conftest import (
    BASE_URL,
    assert_share_copies,
    create_post_with_files,
    paste_image,
)
def create_post(page, topic="random", content="Test post content", title=None):
    page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
    page.locator(".feed-fab").first.wait_for(state="visible", timeout=10000)
    page.locator(".feed-fab").first.click()
    page.check(f"input[value='{topic}']")
    page.fill("#post-content", content)
    if title:
        page.fill("#post-title", title)
    page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
    page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
def _png_bytes(color=(0, 128, 255), size=(600, 400)):
    import io
    from PIL import Image

    buf = io.BytesIO()
    Image.new("RGB", size, color).save(buf, "PNG")
    return buf.getvalue()


def _gif_bytes():
    import io
    from PIL import Image

    frames = [Image.new("RGB", (40, 40), c) for c in ((255, 0, 0), (0, 0, 255))]
    buf = io.BytesIO()
    frames[0].save(
        buf, "GIF", save_all=True, append_images=frames[1:], duration=120, loop=0
    )
    return buf.getvalue()


def _profile_stars(page, username):
    page.goto(f"{BASE_URL}/profile/{username}", wait_until="domcontentloaded")
    value = page.locator(
        ".profile-stat:has(.profile-stat-label:has-text('Stars')) .profile-stat-value"
    ).first
    return int(value.text_content().strip())


def test_post_detail_page(alice):
    page, _ = alice
    create_post(page, "random", "Detail page test content")
    assert page.is_visible("text=Detail page test content")
    assert page.is_visible("text=Back to Feed")


def test_create_post_modal_targets_modal_textarea_on_populated_feed(alice):
    page, _ = alice
    create_post(page, "devlog", "Seed post so the feed renders comment forms")
    page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
    page.locator(".feed-fab").first.wait_for(state="visible", timeout=10000)
    assert page.locator("textarea[name='content']").count() > 1
    page.locator(".feed-fab").first.click()
    modal_content = page.locator("#create-post-modal textarea[name='content']")
    modal_content.wait_for(state="visible", timeout=10000)
    modal_content.click()
    modal_content.fill("Modal scoped body well over ten characters long.")
    page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
    page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
    assert page.is_visible("text=Modal scoped body well over ten characters long.")


def test_post_topic_badge(alice):
    page, _ = alice
    create_post(page, "question", "Question post for badge check")
    badge = page.locator(".badge-question")
    assert badge.is_visible()


def test_post_author_info(alice):
    page, user = alice
    create_post(page, "fun", "Post with author check")
    assert page.is_visible(f"text={user['username']}")


def test_post_vote_button(alice):
    page, _ = alice
    create_post(page, "devlog", "Votable post")
    vote_btn = page.locator(".post-action-btn.vote-up").first
    assert vote_btn.is_visible()


def test_post_vote_increment(alice):
    page, _ = alice
    create_post(page, "showcase", "Vote increment test")
    page.locator(".post-action-btn.vote-up").first.click()
    expect(page.locator(".post-vote-count").first).to_have_text("1")


def test_post_upvote_voted_state_persists(alice):
    page, _ = alice
    create_post(page, "showcase", "Upvote persistence test")
    with page.expect_response(
        lambda r: "/votes/" in r.url and r.request.method == "POST"
    ):
        page.locator(".post-action-btn.vote-up").first.click()
    expect(page.locator(".post-vote-count").first).to_have_text("1")
    page.reload(wait_until="domcontentloaded")
    expect(page.locator(".post-action-btn.vote-up").first).to_have_class(
        re.compile(r"\bvoted\b")
    )
    assert "voted" not in (
        page.locator(".post-action-btn.vote-down").first.get_attribute("class") or ""
    )


def test_post_downvote_voted_state_persists(alice):
    page, _ = alice
    create_post(page, "showcase", "Downvote persistence test")
    with page.expect_response(
        lambda r: "/votes/" in r.url and r.request.method == "POST"
    ):
        page.locator(".post-action-btn.vote-down").first.click()
    expect(page.locator(".post-vote-count").first).to_have_text("-1")
    page.reload(wait_until="domcontentloaded")
    expect(page.locator(".post-action-btn.vote-down").first).to_have_class(
        re.compile(r"\bvoted\b")
    )
    assert "voted" not in (
        page.locator(".post-action-btn.vote-up").first.get_attribute("class") or ""
    )


def test_comment_voted_state_persists(alice):
    page, _ = alice
    create_post(page, "devlog", "Post for comment vote persistence")
    textarea = page.locator(".comment-form textarea[name='content']")
    textarea.fill("Comment whose vote should persist")
    page.locator(".comment-form button:has-text('Post')").click()
    expect(
        page.locator(".comment-text:has-text('Comment whose vote should persist')")
    ).to_be_visible()
    with page.expect_response(
        lambda r: "/votes/" in r.url and r.request.method == "POST"
    ):
        page.locator(".comment-vote-btn").first.click()
    expect(page.locator(".comment-vote-btn").first).to_have_class(
        re.compile(r"\bvoted\b")
    )
    page.reload(wait_until="domcontentloaded")
    expect(page.locator(".comment-vote-btn").first).to_have_class(
        re.compile(r"\bvoted\b")
    )


def test_mention_first_match_highlighted(alice):
    page, _ = alice
    create_post(page, "random", "Mention highlight test post")
    textarea = page.locator(".comment-form textarea[name='content']").first
    textarea.wait_for(state="visible", timeout=10000)
    textarea.fill("@bob")
    item = page.locator(".mention-dropdown-item:has-text('bob_test')").first
    item.wait_for(state="visible", timeout=5000)
    expect(item).to_have_class(re.compile(r"\bactive\b"))


def test_mention_tab_inserts_first_match(alice):
    page, _ = alice
    create_post(page, "random", "Mention tab insert test post")
    textarea = page.locator(".comment-form textarea[name='content']").first
    textarea.wait_for(state="visible", timeout=10000)
    textarea.fill("@bob")
    page.locator(".mention-dropdown-item:has-text('bob_test')").first.wait_for(
        state="visible", timeout=5000
    )
    textarea.press("Tab")
    expect(textarea).to_have_value(re.compile(r"^@bob_test\s"))


def test_mention_arrow_then_enter_inserts(alice):
    page, _ = alice
    create_post(page, "random", "Mention arrow nav test post")
    textarea = page.locator(".comment-form textarea[name='content']").first
    textarea.wait_for(state="visible", timeout=10000)
    textarea.fill("@bob")
    page.locator(".mention-dropdown-item:has-text('bob_test')").first.wait_for(
        state="visible", timeout=5000
    )
    textarea.press("ArrowDown")
    selected = page.locator(".mention-dropdown-item.active").first
    selected.wait_for(state="visible", timeout=5000)
    username = selected.get_attribute("data-username")
    textarea.press("Enter")
    expect(textarea).to_have_value(re.compile(rf"@{re.escape(username)}\s"))


def test_mention_escape_closes_dropdown(alice):
    page, _ = alice
    create_post(page, "random", "Mention escape test post")
    textarea = page.locator(".comment-form textarea[name='content']").first
    textarea.wait_for(state="visible", timeout=10000)
    textarea.fill("@bob")
    item = page.locator(".mention-dropdown-item:has-text('bob_test')").first
    item.wait_for(state="visible", timeout=5000)
    textarea.press("Escape")
    expect(item).to_be_hidden()


def test_profile_stars_reflect_content_votes(alice):
    page, user = alice
    before = _profile_stars(page, user["username"])
    create_post(page, "devlog", "Reputation contribution post")
    with page.expect_response(
        lambda r: "/votes/" in r.url and r.request.method == "POST"
    ):
        page.locator(".post-action-btn.vote-up").first.click()
    expect(page.locator(".post-vote-count").first).to_have_text("1")
    after = _profile_stars(page, user["username"])
    assert after == before + 1, f"expected stars {before + 1}, got {after}"


def test_post_comments_section(alice):
    page, _ = alice
    create_post(page, "rant", "Comment section test")
    assert page.is_visible("text=Comments")
    assert page.is_visible("textarea[placeholder='Your opinion goes here...']")


def test_add_comment(alice):
    page, _ = alice
    create_post(page, "random", "Post for commenting")
    textarea = page.locator(".comment-form textarea[name='content']")
    textarea.fill("This is a test comment from Playwright")
    page.locator(".comment-form button:has-text('Post')").click()
    expect(
        page.locator(".comment-text:has-text('This is a test comment from Playwright')")
    ).to_be_visible()


def test_comment_voting(alice):
    page, _ = alice
    create_post(page, "devlog", "Post for comment voting")
    textarea = page.locator(".comment-form textarea[name='content']")
    textarea.fill("Votable comment")
    page.locator(".comment-form button:has-text('Post')").click()
    expect(page.locator(".comment-text:has-text('Votable comment')")).to_be_visible()
    vote_btns = page.locator(".comment-vote-btn")
    upvote = vote_btns.first
    upvote.click()
    expect(vote_btns.first).to_have_class(re.compile(r"\bvoted\b"))


def test_delete_own_comment(alice):
    page, _ = alice
    create_post(page, "fun", "Post for comment deletion")
    textarea = page.locator(".comment-form textarea[name='content']")
    textarea.fill("Comment to delete")
    page.locator(".comment-form button:has-text('Post')").click()
    comment = page.locator(".comment-text:has-text('Comment to delete')")
    expect(comment).to_be_visible()
    delete_btn = page.locator(".comment-action-btn:has-text('Delete')").last
    expect(delete_btn).to_be_visible()
    delete_btn.click()
    page.locator(".dialog-overlay.visible .dialog-confirm").click()
    expect(comment).to_have_count(0)


def test_comment_form_elements(alice):
    page, _ = alice
    create_post(page, "random", "Post for checking comment form")
    assert page.is_visible("textarea[placeholder='Your opinion goes here...']")


def test_share_button(alice):
    page, _ = alice
    create_post(page, "showcase", "Share button check")
    assert_share_copies(page, "/posts/")


def test_multiple_comments_on_post(alice):
    page, _ = alice
    create_post(page, "devlog", "Post with many comments")
    for i in range(3):
        page.locator(".comment-form textarea[name='content']").fill(
            f"Comment number {i + 1}"
        )
        page.locator(".comment-form button:has-text('Post')").click()
        expect(
            page.locator(f".comment-text:has-text('Comment number {i + 1}')")
        ).to_be_visible()
    assert page.is_visible("text=Comment number 1")
    assert page.is_visible("text=Comment number 3")


def test_comment_and_vote_then_delete(alice):
    page, _ = alice
    create_post(page, "showcase", "Full comment lifecycle post")
    page.locator(".comment-form textarea[name='content']").fill("Lifecycle comment")
    page.locator(".comment-form button:has-text('Post')").click()
    comment = page.locator(".comment-text:has-text('Lifecycle comment')")
    expect(comment).to_be_visible()

    vote_up = page.locator(".comment-vote-btn").first
    vote_up.click()
    expect(page.locator(".comment-vote-btn").first).to_have_class(
        re.compile(r"\bvoted\b")
    )

    delete_btn = page.locator(".comment-action-btn:has-text('Delete')").last
    expect(delete_btn).to_be_visible()
    delete_btn.click()
    page.locator(".dialog-overlay.visible .dialog-confirm").click()
    expect(comment).to_have_count(0)


def test_comment_reply_inline_form(alice):
    page, _ = alice
    create_post(page, "devlog", "Post for inline reply")
    page.locator(".comment-form textarea[name='content']").fill("Parent comment here")
    page.locator(".comment-form button:has-text('Post')").click()
    expect(
        page.locator(".comment-text:has-text('Parent comment here')")
    ).to_be_visible()
    page.locator(".comment [data-action='reply']").first.click()
    reply_form = page.locator(".comment-reply-form").first
    expect(reply_form).to_be_visible()
    reply_form.locator("textarea[name='content']").fill("Inline reply text")
    reply_form.locator("button:has-text('Post')").click()
    expect(
        page.locator(".comment-replies .comment-text:has-text('Inline reply text')")
    ).to_be_visible()


def test_comment_reply_toggle_and_cancel(alice):
    page, _ = alice
    create_post(page, "devlog", "Post for reply toggle")
    page.locator(".comment-form textarea[name='content']").fill("Toggle parent")
    page.locator(".comment-form button:has-text('Post')").click()
    expect(page.locator(".comment-text:has-text('Toggle parent')")).to_be_visible()
    reply_btn = page.locator(".comment [data-action='reply']").first
    reply_btn.click()
    expect(page.locator(".comment-reply-form")).to_have_count(1)
    reply_btn.click()
    expect(page.locator(".comment-reply-form")).to_have_count(0)
    reply_btn.click()
    expect(page.locator(".comment-reply-form")).to_have_count(1)
    page.locator(".comment-reply-cancel").first.click()
    expect(page.locator(".comment-reply-form")).to_have_count(0)


def test_comment_create_scrolls_to_anchor(alice):
    page, _ = alice
    create_post(page, "devlog", "Post for comment anchor")
    page.locator(".comment-form textarea[name='content']").fill("Anchor comment text")
    page.locator(".comment-form button:has-text('Post')").click()
    expect(
        page.locator(".comment-text:has-text('Anchor comment text')")
    ).to_be_visible()
    assert "#comment-" in page.url
    comment_uid = page.url.split("#comment-")[1]
    page.wait_for_selector(f"#comment-{comment_uid}.comment-highlight", timeout=5000)
    expect(page.locator(f"#comment-{comment_uid}")).to_contain_text(
        "Anchor comment text"
    )


def test_post_across_all_topics(alice):
    page, _ = alice
    topics = ["devlog", "showcase", "question", "rant", "fun", "politics"]
    for topic in topics:
        create_post(page, topic, f"Topic test post for {topic}")
        badge = page.locator(f".badge-{topic}")
        expect(badge).to_be_visible()


def test_content_rendering_markdown_and_highlight(alice):
    page, _ = alice
    create_post(
        page, "random", "Hello **world bold** text\n\n```python\nprint('hi')\n```"
    )
    page.locator(".rendered-content strong").first.wait_for(state="visible")
    assert page.locator(".rendered-content pre code").count() >= 1


def test_mention_autocomplete(alice):
    page, _ = alice
    create_post(page, "random", "Post for mention autocomplete here")
    ta = page.locator(".comment-form textarea[data-mention]").first
    ta.click()
    ta.press_sequentially("@bob")
    item = page.locator(".mention-dropdown-item").first
    item.wait_for(state="visible")
    assert "bob" in item.inner_text().lower()


def test_emoji_picker_opens(alice):
    page, _ = alice
    create_post(page, "random", "Post for emoji picker here")
    page.locator(".emoji-toggle-btn").first.click()
    page.locator(".emoji-picker-wrapper").first.wait_for(state="visible")
    assert page.locator("emoji-picker").first.is_visible()


def test_paste_image_attaches_in_comment_form(alice):
    page, _ = alice
    create_post(page, "random", "Post for pasted comment attachment")
    page.locator(".comment-form dp-upload .dp-upload-btn").first.wait_for(
        state="visible", timeout=10000
    )
    paste_image(page, ".comment-form textarea[name='content']")
    page.locator(".comment-form dp-upload .dp-upload-count").first.wait_for(
        state="visible", timeout=15000
    )
    expect(
        page.locator(".comment-form dp-upload input[name='attachment_uids']")
    ).to_have_value(re.compile(r".+"))


def test_single_image_post_shows_the_original_full_size(alice):
    page, _ = alice
    create_post_with_files(
        page,
        "Post carrying exactly one image",
        [{"name": "shot.png", "mimeType": "image/png", "buffer": _png_bytes()}],
    )
    gallery = page.locator(".attachment-gallery.single")
    gallery.wait_for(state="visible", timeout=10000)
    img = gallery.locator(".gallery-thumb").first
    src = img.get_attribute("src")
    assert "_thumb" not in src, f"hero image served the 200px thumbnail: {src}"
    assert src == img.get_attribute("data-full")


def test_multiple_image_post_keeps_thumbnails(alice):
    page, _ = alice
    create_post_with_files(
        page,
        "Post carrying two images",
        [
            {"name": "one.png", "mimeType": "image/png", "buffer": _png_bytes()},
            {
                "name": "two.png",
                "mimeType": "image/png",
                "buffer": _png_bytes((200, 30, 90)),
            },
        ],
        expected_count=2,
    )
    page.locator(".attachment-gallery").first.wait_for(state="visible", timeout=10000)
    assert page.locator(".attachment-gallery.single").count() == 0
    thumbs = page.locator(".attachment-gallery .gallery-thumb")
    assert thumbs.count() == 2
    for i in range(thumbs.count()):
        assert "_thumb" in thumbs.nth(i).get_attribute("src")


def test_animated_gif_post_serves_the_original_file(alice):
    page, _ = alice
    create_post_with_files(
        page,
        "Post carrying an animated gif",
        [{"name": "loop.gif", "mimeType": "image/gif", "buffer": _gif_bytes()}],
    )
    img = page.locator(".attachment-gallery .gallery-thumb").first
    img.wait_for(state="visible", timeout=10000)
    src = img.get_attribute("src")
    assert src.endswith(".gif"), f"animation lost, served {src}"


def test_attachment_upload_ui(alice):
    import io
    from PIL import Image

    page, _ = alice
    create_post(page, "random", "Post for attachment upload UI")
    buf = io.BytesIO()
    Image.new("RGB", (4, 4), (0, 128, 255)).save(buf, "PNG")
    file_input = page.locator(".comment-form dp-upload .dp-upload-input").first
    file_input.set_input_files(
        {"name": "pic.png", "mimeType": "image/png", "buffer": buf.getvalue()}
    )
    page.locator(".comment-form dp-upload .dp-upload-count").first.wait_for(
        state="visible", timeout=15000
    )
    expect(
        page.locator(".comment-form dp-upload input[name='attachment_uids']")
    ).to_have_value(re.compile(r".+"))
