"""
Step 2b — Schema Hardening Tests
- test_suffix_table_exists: The suffix table is created in the schema
- test_exercise_title_unique: Duplicate exercise titles are rejected
- test_combo_title_unique: Duplicate combo titles are rejected
- test_session_title_unique: Duplicate session titles are rejected
- test_collection_label_unique: Duplicate collection labels are rejected
- test_suffix_crud: Full CRUD cycle on the suffix table
- test_suffix_unique_constraint: Duplicate suffixes are rejected
- test_suffix_user_uuid_primary_key: Duplicate user_uuid is rejected
- test_unique_constraint_allows_update: Updating a record to its own title succeeds
- test_unique_constraint_across_updates: Updating a title to an existing title fails
"""
from playwright.sync_api import Page, expect


def _wait_for_db(page: Page, app_url: str):
    """Navigate and wait for the database to be ready."""
    page.goto(app_url)
    page.wait_for_function(
        "document.documentElement.getAttribute('data-db-ready') === 'true'", timeout=15000
    )


def test_suffix_table_exists(page: Page, app_url: str):
    """The suffix table is created in the schema."""
    _wait_for_db(page, app_url)

    tables = page.evaluate(
        """
        window.__db.selectAll(
            "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
        )
    """
    )
    table_names = [t["name"] for t in tables]
    assert "suffix" in table_names, f"Table 'suffix' not found. Found: {table_names}"

    # Verify table structure
    columns = page.evaluate(
        """
        window.__db.selectAll("PRAGMA table_info(suffix)")
    """
    )
    col_names = [c["name"] for c in columns]
    assert "user_uuid" in col_names, "Column 'user_uuid' not found in suffix table"
    assert "user_name" in col_names, "Column 'user_name' not found in suffix table"
    assert "suffix" in col_names, "Column 'suffix' not found in suffix table"


def test_exercise_title_unique(page: Page, app_url: str):
    """Duplicate exercise titles are rejected by the UNIQUE constraint."""
    _wait_for_db(page, app_url)

    # Create first exercise
    ex_id = page.evaluate(
        """
        window.__repos.exercises.create({
            title: 'Unique Push-ups',
            description: '',
            created_by: 'test'
        })
    """
    )
    assert ex_id is not None

    # Try to create a duplicate
    error = page.evaluate(
        """
        (async () => {
            try {
                await window.__repos.exercises.create({
                    title: 'Unique Push-ups',
                    description: 'Duplicate',
                    created_by: 'test'
                });
                return null;
            } catch (e) {
                return e.message;
            }
        })()
    """
    )
    assert error is not None, "Duplicate exercise title was allowed"
    assert "UNIQUE" in error or "unique" in error.lower(), f"Unexpected error: {error}"

    # Cleanup
    page.evaluate(f"window.__repos.exercises.delete('{ex_id}')")


def test_combo_title_unique(page: Page, app_url: str):
    """Duplicate combo titles are rejected by the UNIQUE constraint."""
    _wait_for_db(page, app_url)

    combo_id = page.evaluate(
        """
        window.__repos.combos.create({
            title: 'Unique Combo',
            description: '',
            type: 'NONE',
            created_by: 'test'
        })
    """
    )
    assert combo_id is not None

    error = page.evaluate(
        """
        (async () => {
            try {
                await window.__repos.combos.create({
                    title: 'Unique Combo',
                    description: 'Duplicate',
                    type: 'NONE',
                    created_by: 'test'
                });
                return null;
            } catch (e) {
                return e.message;
            }
        })()
    """
    )
    assert error is not None, "Duplicate combo title was allowed"
    assert "UNIQUE" in error or "unique" in error.lower(), f"Unexpected error: {error}"

    # Cleanup
    page.evaluate(f"window.__repos.combos.delete('{combo_id}')")


def test_session_title_unique(page: Page, app_url: str):
    """Duplicate session titles are rejected by the UNIQUE constraint."""
    _wait_for_db(page, app_url)

    session_id = page.evaluate(
        """
        window.__repos.sessions.create({
            title: 'Unique Session',
            description: '',
            created_by: 'test'
        })
    """
    )
    assert session_id is not None

    error = page.evaluate(
        """
        (async () => {
            try {
                await window.__repos.sessions.create({
                    title: 'Unique Session',
                    description: 'Duplicate',
                    created_by: 'test'
                });
                return null;
            } catch (e) {
                return e.message;
            }
        })()
    """
    )
    assert error is not None, "Duplicate session title was allowed"
    assert "UNIQUE" in error or "unique" in error.lower(), f"Unexpected error: {error}"

    # Cleanup
    page.evaluate(f"window.__repos.sessions.delete('{session_id}')")


def test_collection_label_unique(page: Page, app_url: str):
    """Duplicate collection labels are rejected by the UNIQUE constraint."""
    _wait_for_db(page, app_url)

    coll_id = page.evaluate(
        """
        window.__repos.collections.create({
            label: 'Unique Collection',
            created_by: 'test'
        })
    """
    )
    assert coll_id is not None

    error = page.evaluate(
        """
        (async () => {
            try {
                await window.__repos.collections.create({
                    label: 'Unique Collection',
                    created_by: 'test'
                });
                return null;
            } catch (e) {
                return e.message;
            }
        })()
    """
    )
    assert error is not None, "Duplicate collection label was allowed"
    assert "UNIQUE" in error or "unique" in error.lower(), f"Unexpected error: {error}"

    # Cleanup
    page.evaluate(f"window.__repos.collections.delete('{coll_id}')")


def test_suffix_crud(page: Page, app_url: str):
    """Full CRUD cycle on the suffix table."""
    _wait_for_db(page, app_url)

    # Insert
    page.evaluate(
        """
        window.__db.run(
            "INSERT INTO suffix (user_uuid, user_name, suffix) VALUES (?, ?, ?)",
            ['uuid-001', 'Alice', 'AL']
        )
    """
    )

    # Read
    row = page.evaluate(
        """
        window.__db.selectAll("SELECT * FROM suffix WHERE user_uuid = 'uuid-001'")
    """
    )
    assert len(row) == 1, f"Expected 1 row, got {len(row)}"
    assert row[0]["user_uuid"] == "uuid-001"
    assert row[0]["user_name"] == "Alice"
    assert row[0]["suffix"] == "AL"

    # Update
    page.evaluate(
        """
        window.__db.run(
            "UPDATE suffix SET suffix = ?, user_name = ? WHERE user_uuid = ?",
            ['ALC', 'Alice C.', 'uuid-001']
        )
    """
    )
    updated = page.evaluate(
        """
        window.__db.selectAll("SELECT * FROM suffix WHERE user_uuid = 'uuid-001'")
    """
    )
    assert updated[0]["suffix"] == "ALC"
    assert updated[0]["user_name"] == "Alice C."

    # Delete
    page.evaluate(
        """
        window.__db.run("DELETE FROM suffix WHERE user_uuid = 'uuid-001'")
    """
    )
    deleted = page.evaluate(
        """
        window.__db.selectAll("SELECT * FROM suffix WHERE user_uuid = 'uuid-001'")
    """
    )
    assert len(deleted) == 0, "Suffix row still exists after deletion"


def test_suffix_unique_constraint(page: Page, app_url: str):
    """Duplicate suffix values are rejected by the UNIQUE constraint."""
    _wait_for_db(page, app_url)

    # Insert first
    page.evaluate(
        """
        window.__db.run(
            "INSERT INTO suffix (user_uuid, user_name, suffix) VALUES (?, ?, ?)",
            ['uuid-100', 'Bob', 'BB']
        )
    """
    )

    # Try duplicate suffix with different user
    error = page.evaluate(
        """
        (async () => {
            try {
                await window.__db.run(
                    "INSERT INTO suffix (user_uuid, user_name, suffix) VALUES (?, ?, ?)",
                    ['uuid-200', 'Charlie', 'BB']
                );
                return null;
            } catch (e) {
                return e.message;
            }
        })()
    """
    )
    assert error is not None, "Duplicate suffix was allowed"
    assert "UNIQUE" in error or "unique" in error.lower(), f"Unexpected error: {error}"

    # Cleanup
    page.evaluate("""window.__db.run("DELETE FROM suffix WHERE user_uuid = 'uuid-100'")""")


def test_suffix_user_uuid_primary_key(page: Page, app_url: str):
    """Duplicate user_uuid (primary key) is rejected."""
    _wait_for_db(page, app_url)

    page.evaluate(
        """
        window.__db.run(
            "INSERT INTO suffix (user_uuid, user_name, suffix) VALUES (?, ?, ?)",
            ['uuid-pk-test', 'Dave', 'DV']
        )
    """
    )

    error = page.evaluate(
        """
        (async () => {
            try {
                await window.__db.run(
                    "INSERT INTO suffix (user_uuid, user_name, suffix) VALUES (?, ?, ?)",
                    ['uuid-pk-test', 'Dave Again', 'DV2']
                );
                return null;
            } catch (e) {
                return e.message;
            }
        })()
    """
    )
    assert error is not None, "Duplicate user_uuid was allowed"

    # Cleanup
    page.evaluate("""window.__db.run("DELETE FROM suffix WHERE user_uuid = 'uuid-pk-test'")""")


def test_unique_constraint_allows_update(page: Page, app_url: str):
    """Updating a record to its own title succeeds (UNIQUE constraint does not block self-update)."""
    _wait_for_db(page, app_url)

    ex_id = page.evaluate(
        """
        window.__repos.exercises.create({
            title: 'Self Update Test',
            description: '',
            created_by: 'test'
        })
    """
    )

    # Update to same title should work
    error = page.evaluate(
        f"""
        (async () => {{
            try {{
                await window.__repos.exercises.update('{ex_id}', {{
                    title: 'Self Update Test',
                    description: 'Updated description'
                }});
                return null;
            }} catch (e) {{
                return e.message;
            }}
        }})()
    """
    )
    assert error is None, f"Self-update was rejected: {error}"

    # Cleanup
    page.evaluate(f"window.__repos.exercises.delete('{ex_id}')")


def test_unique_constraint_across_updates(page: Page, app_url: str):
    """Updating a title to match another existing title fails."""
    _wait_for_db(page, app_url)

    id_a = page.evaluate(
        """
        window.__repos.exercises.create({
            title: 'Exercise A',
            description: '',
            created_by: 'test'
        })
    """
    )
    id_b = page.evaluate(
        """
        window.__repos.exercises.create({
            title: 'Exercise B',
            description: '',
            created_by: 'test'
        })
    """
    )

    # Try to update B's title to A's title
    error = page.evaluate(
        f"""
        (async () => {{
            try {{
                await window.__repos.exercises.update('{id_b}', {{
                    title: 'Exercise A'
                }});
                return null;
            }} catch (e) {{
                return e.message;
            }}
        }})()
    """
    )
    assert error is not None, "Updating to a duplicate title was allowed"
    assert "UNIQUE" in error or "unique" in error.lower(), f"Unexpected error: {error}"

    # Cleanup
    page.evaluate(f"window.__repos.exercises.delete('{id_a}')")
    page.evaluate(f"window.__repos.exercises.delete('{id_b}')")
