from datetime import datetime, timezone
from typing import Any, Dict
import uuid

import pytest

from backend.app.db.models.hubspot_deal_data import HubspotDealData
from etl.hubspot.hubspot_deal_differ import HubspotDealDiffer


BASE_TIME = datetime(2025, 12, 1, 12, 0, 0)


def make_old_deal(**overrides: Any) -> HubspotDealData:
    return HubspotDealData(
        id=overrides.get("id", uuid.uuid4()),
        deal_id="1",
        created_at=BASE_TIME,
        updated_at=BASE_TIME,
        **{k: v for k, v in overrides.items() if k != "id"},
    )


def make_new_deal(deal_id: uuid.UUID, **overrides: Any) -> Dict[str, str]:
    return {
        "id": str(deal_id),
        "deal_id": "1",
        "created_at": BASE_TIME.isoformat(),
        "updated_at": datetime(2025, 12, 1, 12, 30, 0).isoformat(),
        **overrides,
    }


# ====================
# PASHUB TRIGGER TESTS
# ====================


@pytest.mark.parametrize(
    "new_overrides,expected",
    [
        ({"outcome_notes": "test note"}, False),
    ],
)
def test_pashub_trigger__outcome_note_added__returns_false(
    new_overrides: Dict[str, str],
    expected: bool,
) -> None:
    deal_id = uuid.uuid4()
    old_deal = make_old_deal(id=deal_id)
    new_deal = make_new_deal(deal_id, **new_overrides)

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        == expected
    )


@pytest.mark.parametrize(
    "old_overrides,new_overrides,expected",
    [
        (
            {"pashub_link": "www.google.co.uk"},
            {"pashub_link": "www.bbc.co.uk"},
            True,
        ),
    ],
)
def test_pashub_trigger__pashub_link_changed__returns_true(
    old_overrides: Dict[str, str],
    new_overrides: Dict[str, str],
    expected: bool,
) -> None:
    deal_id = uuid.uuid4()
    old_deal = make_old_deal(id=deal_id, **old_overrides)
    new_deal = make_new_deal(deal_id, **new_overrides)

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        == expected
    )


@pytest.mark.parametrize(
    "coordination_status,expected",
    [
        ("(v1) ioe/mtp complete", True),
        ("(v2) ioe/mtp complete", True),
    ],
)
def test_pashub_trigger__coordination_completed_and_pashub_link_set__returns_true(
    coordination_status: str,
    expected: bool,
) -> None:
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        pashub_link="www.google.co.uk",
        coordination_status="random",
    )

    new_deal = make_new_deal(
        deal_id,
        pashub_link="www.google.co.uk",
        **{"coordination_status__stage_1_": coordination_status},
    )

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        == expected
    )


def test_pashub_trigger__coordination_completed_and_pashub_link_not_set__returns_false() -> (
    None
):
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        coordination_status="random",
    )

    new_deal = make_new_deal(
        deal_id,
        coordination_status="v2 ioe/mtp complete",
    )

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        is False
    )


def test_pashub_trigger__design_completed_and_pashub_link_set__returns_true() -> None:
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        pashub_link="www.google.co.uk",
    )

    new_deal = make_new_deal(
        deal_id,
        pashub_link="www.google.co.uk",
        retrofit_design_status="uploaded",
    )

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        is True
    )


def test_pashub_trigger__design_completed_and_pashub_link_not_set__returns_false() -> (
    None
):
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(id=deal_id)

    new_deal = make_new_deal(
        deal_id,
        retrofit_design_status="uploaded",
    )

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        is False
    )


@pytest.mark.parametrize(
    "lodgement_status,expected",
    [
        ("lodgement complete", True),
        ("measures lodged", True),
    ],
)
def test_pashub_trigger__lodgement_completed_and_pashub_link_set__returns_true(
    lodgement_status: str,
    expected: bool,
) -> None:
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        pashub_link="www.google.co.uk",
    )

    new_deal = make_new_deal(
        deal_id,
        pashub_link="www.google.co.uk",
        lodgement_status=lodgement_status,
    )

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        == expected
    )


def test_pashub_trigger__lodgement_completed_and_pashub_link_not_set__returns_false() -> (
    None
):
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(id=deal_id)

    new_deal = make_new_deal(
        deal_id,
        design_status="lodgement complete",
    )

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        is False
    )


def test_pashub_trigger__coordination_design_lodgement_not_completed_and_pashub_link_set__returns_false() -> (
    None
):
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        pashub_link="www.google.co.uk",
    )

    new_deal = make_new_deal(
        deal_id,
        pashub_link="www.google.co.uk",
        coordination_status="not uploaded",
        design_status="not uploaded",
        lodgement_status="not uploaded",
    )

    assert (
        HubspotDealDiffer.check_for_pashub_trigger(
            new_deal=new_deal,
            old_deal=old_deal,
        )
        is False
    )


# ==========================
# MAGICPLAN TRIGGER TESTS
# ==========================


def test_magicplan_trigger__outcome_transitions_to_surveyed__returns_true() -> None:
    deal_id = uuid.uuid4()

    # Arrange
    old_deal = make_old_deal(id=deal_id, outcome="assessed")
    new_deal = make_new_deal(deal_id, outcome="surveyed")

    # Act
    result = HubspotDealDiffer.check_for_magicplan_trigger(
        new_deal=new_deal,
        old_deal=old_deal,
    )

    # Assert
    assert result is True


def test_magicplan_trigger__outcome_already_surveyed__returns_false() -> None:
    deal_id = uuid.uuid4()

    # Arrange
    old_deal = make_old_deal(id=deal_id, outcome="surveyed")
    new_deal = make_new_deal(deal_id, outcome="surveyed")

    # Act
    result = HubspotDealDiffer.check_for_magicplan_trigger(
        new_deal=new_deal,
        old_deal=old_deal,
    )

    # Assert
    assert result is False


def test_magicplan_trigger__outcome_transitions_to_non_surveyed__returns_false() -> None:
    deal_id = uuid.uuid4()

    # Arrange
    old_deal = make_old_deal(id=deal_id, outcome="assessed")
    new_deal = make_new_deal(deal_id, outcome="assessed")

    # Act
    result = HubspotDealDiffer.check_for_magicplan_trigger(
        new_deal=new_deal,
        old_deal=old_deal,
    )

    # Assert
    assert result is False


def test_magicplan_trigger__outcome_surveyed_uppercase__returns_true() -> None:
    deal_id = uuid.uuid4()

    # Arrange
    old_deal = make_old_deal(id=deal_id, outcome="assessed")
    new_deal = make_new_deal(deal_id, outcome="SURVEYED")

    # Act
    result = HubspotDealDiffer.check_for_magicplan_trigger(
        new_deal=new_deal,
        old_deal=old_deal,
    )

    # Assert
    assert result is True


# =======================
# DB UPDATE TRIGGER TESTS
# =======================


def test_db_update_trigger__no_changes__returns_false() -> None:
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        dealname="Test Deal",
        dealstage="stage_1",
        outcome="won",
    )

    new_deal = make_new_deal(
        deal_id,
        hs_object_id="1",
        dealname="Test Deal",
        dealstage="stage_1",
        outcome="won",
    )

    result = HubspotDealDiffer.check_for_db_update_trigger(
        new_deal=new_deal,
        new_company=None,
        new_listing=None,
        old_deal=old_deal,
    )

    assert result is False


def test_db_update_trigger__dealname_changed__returns_true() -> None:
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        dealname="Old Name",
    )

    new_deal = make_new_deal(
        deal_id,
        hs_object_id="1",
        dealname="New Name",
    )

    result = HubspotDealDiffer.check_for_db_update_trigger(
        new_deal=new_deal,
        new_company=None,
        new_listing=None,
        old_deal=old_deal,
    )

    assert result is True


def test_db_update_trigger__company_changed__returns_true() -> None:
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        company_id="old_company",
    )

    new_deal = make_new_deal(
        deal_id,
        hs_object_id="1",
    )

    new_company = "new_company"

    result = HubspotDealDiffer.check_for_db_update_trigger(
        new_deal=new_deal,
        new_company=new_company,
        new_listing=None,
        old_deal=old_deal,
    )

    assert result is True


def test_db_update_trigger__missing_hubspot_timezone__returns_false() -> None:
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        design_completion_date=datetime(2025, 11, 3, 0, 0, tzinfo=timezone.utc),
    )

    new_deal = make_new_deal(
        deal_id,
        hs_object_id="1",
        design_completion_date=datetime(2025, 11, 3, 0, 0).isoformat(),
    )

    result = HubspotDealDiffer.check_for_db_update_trigger(
        new_deal=new_deal,
        new_company=None,
        new_listing=None,
        old_deal=old_deal,
    )

    assert result is False


def test_db_update_trigger__listing_changed__returns_true() -> None:
    deal_id = uuid.uuid4()

    old_deal = make_old_deal(
        id=deal_id,
        listing_id="abc",
    )

    new_deal = make_new_deal(
        deal_id,
        hs_object_id="1",
    )

    new_listing = {"listing_id": "xyz"}

    result = HubspotDealDiffer.check_for_db_update_trigger(
        new_deal=new_deal,
        new_company=None,
        new_listing=new_listing,
        old_deal=old_deal,
    )

    assert result is True
