"""Stored procedure tests against the live CData Connect AI environment.

All 8 stored procedures defined in PostgreSQLPythonConnectorIntegrationTest:
  1. sp_create_user            — inserts a new user row
  2. sp_update_contact_info    — updates phone, email, address, city, country
  3. sp_update_job_info        — updates occupation and company
  4. sp_bulk_update_company    — renames all users from one company to another
  5. sp_activate_user          — sets is_active = TRUE
  6. sp_deactivate_user        — sets is_active = FALSE
  7. sp_transfer_users_company — moves users between companies with country filter
  8. sp_refresh_stale_records  — updates updated_at for stale active users

Notes:
  - sp_create_user uses cursor.execute() with an inline CALL statement because
    callproc() sends the p_dob date string as VARCHAR (dataType 5), which
    PostgreSQL rejects for DATE parameters.  All other procedures use callproc().
  - Each test creates isolated data and cleans up in a finally block.
"""

from tests.integration_live.helpers import (
    SP,
    TABLE,
    cleanup,
    company,
    get_user_id,
    insert_user,
    make_email,
)


class TestStoredProcedures:
    # 1 -----------------------------------------------------------------------
    def test_sp_create_user(self, cur):
        e = make_email("spcreate")
        try:
            cur.execute(
                f"CALL {SP}.[sp_create_user]("
                f"'SpFirst', 'SpLast', '{e}', '+1-555-4001', "
                f"'1988-07-10', 'Male', 'USA', 'Denver', '4 Proc Street', "
                f"'Architect', 'ProcCorp')"
            )
            cur.execute(
                f"SELECT first_name, last_name, email FROM {TABLE} WHERE email = '{e}'"
            )
            rows = cur.fetchall()
            assert len(rows) == 1, "sp_create_user must insert exactly one row"
            assert rows[0][0] == "SpFirst"
            assert rows[0][1] == "SpLast"
            assert rows[0][2] == e
        finally:
            cleanup(cur, e)

    # 2 -----------------------------------------------------------------------
    def test_sp_update_contact_info(self, cur):
        old_email = make_email("spcontactold")
        new_email = make_email("spcontactnew")
        try:
            insert_user(cur, old_email, phone="+1-000-0000", city="OldCity", country="OldLand")
            user_id = get_user_id(cur, old_email)
            assert user_id is not None

            cur.callproc(
                f"{SP}.[sp_update_contact_info]",
                (user_id, "+1-777-7777", new_email, "99 Updated Blvd", "NewCity", "Newland"),
            )

            cur.execute(f"SELECT phone, city, country FROM {TABLE} WHERE id = {user_id}")
            row = cur.fetchone()
            assert row[0] == "+1-777-7777"
            assert row[1] == "NewCity"
            assert row[2] == "Newland"
        finally:
            cleanup(cur, old_email, new_email)

    # 3 -----------------------------------------------------------------------
    def test_sp_update_job_info(self, cur):
        e = make_email("spjob")
        try:
            insert_user(cur, e, occupation="Junior Dev", company="OldCo")
            user_id = get_user_id(cur, e)
            assert user_id is not None

            cur.callproc(
                f"{SP}.[sp_update_job_info]",
                (user_id, "Staff Engineer", "NewCo Inc"),
            )

            cur.execute(f"SELECT occupation, company FROM {TABLE} WHERE id = {user_id}")
            row = cur.fetchone()
            assert row[0] == "Staff Engineer"
            assert row[1] == "NewCo Inc"
        finally:
            cleanup(cur, e)

    # 4 -----------------------------------------------------------------------
    def test_sp_bulk_update_company(self, cur):
        old_co = company("bulkold")
        new_co = company("bulknew")
        emails = [make_email(f"spbulk{i}") for i in range(3)]
        try:
            for e in emails:
                insert_user(cur, e, company=old_co)

            cur.callproc(f"{SP}.[sp_bulk_update_company]", (old_co, new_co))

            for e in emails:
                cur.execute(f"SELECT company FROM {TABLE} WHERE email = '{e}'")
                row = cur.fetchone()
                assert row is not None
                assert row[0] == new_co, f"Expected company '{new_co}' for {e}"
        finally:
            cleanup(cur, *emails)

    # 5 -----------------------------------------------------------------------
    def test_sp_activate_user(self, cur):
        e = make_email("spactivate")
        try:
            insert_user(cur, e)
            user_id = get_user_id(cur, e)
            assert user_id is not None

            # Manually deactivate so activation is a meaningful state change.
            cur.executemany(
                f"UPDATE {TABLE} SET is_active = FALSE WHERE id = @id",
                [{"@id": user_id}],
            )

            cur.callproc(f"{SP}.[sp_activate_user]", (user_id,))

            cur.execute(f"SELECT is_active FROM {TABLE} WHERE id = {user_id}")
            assert cur.fetchone()[0] is True
        finally:
            cleanup(cur, e)

    # 6 -----------------------------------------------------------------------
    def test_sp_deactivate_user(self, cur):
        e = make_email("spdeact")
        try:
            insert_user(cur, e)
            user_id = get_user_id(cur, e)
            assert user_id is not None

            cur.callproc(f"{SP}.[sp_deactivate_user]", (user_id,))

            cur.execute(f"SELECT is_active FROM {TABLE} WHERE id = {user_id}")
            assert cur.fetchone()[0] is False
        finally:
            cleanup(cur, e)

    # 7 -----------------------------------------------------------------------
    def test_sp_transfer_users_company(self, cur):
        from_co = company("xferfrom")
        to_co   = company("xferto")
        emails  = [make_email(f"spxfer{i}") for i in range(3)]
        try:
            for e in emails:
                insert_user(cur, e, company=from_co, country="USA")

            cur.callproc(
                f"{SP}.[sp_transfer_users_company]",
                (from_co, to_co, "USA"),
            )

            for e in emails:
                cur.execute(f"SELECT company FROM {TABLE} WHERE email = '{e}'")
                row = cur.fetchone()
                assert row is not None
                assert row[0] == to_co, f"Expected company '{to_co}' for {e}"
        finally:
            cleanup(cur, *emails)

    # 8 -----------------------------------------------------------------------
    def test_sp_refresh_stale_records(self, cur):
        e = make_email("sprefresh")
        try:
            insert_user(cur, e)
            user_id = get_user_id(cur, e)
            assert user_id is not None

            # Back-date updated_at; inline timestamp literal, parameterise only id.
            cur.executemany(
                f"UPDATE {TABLE} "
                f"SET updated_at = '2020-01-01 00:00:00', is_active = TRUE "
                f"WHERE id = @id",
                [{"@id": user_id}],
            )
            cur.execute(f"SELECT updated_at FROM {TABLE} WHERE id = {user_id}")
            before = cur.fetchone()[0]

            # Threshold of 1000 days catches our 2020-backdated record.
            cur.callproc(f"{SP}.[sp_refresh_stale_records]", (1000,))

            cur.execute(f"SELECT updated_at FROM {TABLE} WHERE id = {user_id}")
            after = cur.fetchone()[0]
            assert after != before, (
                "sp_refresh_stale_records must update updated_at for stale active records"
            )
        finally:
            cleanup(cur, e)
