import uuid

import pytest
from sqlalchemy.exc import SQLAlchemyError

from app.dao.service_sms_sender_dao import (
    archive_sms_sender,
    dao_add_sms_sender_for_service,
    dao_get_service_sms_senders_by_id,
    dao_get_sms_senders_by_service_id,
    dao_update_service_sms_sender,
    update_existing_sms_sender_with_inbound_number,
)
from app.exceptions import ArchiveValidationError
from app.models import ServiceSmsSender
from tests.app.db import (
    create_inbound_number,
    create_service,
    create_service_sms_sender,
    create_service_with_inbound_number,
)


def test_dao_get_service_sms_senders_id(notify_db_session):
    service = create_service()
    second_sender = dao_add_sms_sender_for_service(
        service_id=service.id, sms_sender="second", is_default=False, inbound_number_id=None
    )
    result = dao_get_service_sms_senders_by_id(service_id=service.id, service_sms_sender_id=second_sender.id)
    assert result.sms_sender == "second"
    assert not result.is_default


def test_dao_get_service_sms_senders_id_raise_exception_when_not_found(notify_db_session):
    service = create_service()
    with pytest.raises(expected_exception=SQLAlchemyError):
        dao_get_service_sms_senders_by_id(service_id=service.id, service_sms_sender_id=uuid.uuid4())


def test_dao_get_service_sms_senders_id_raises_exception_with_archived_sms_sender(notify_db_session):
    service = create_service()
    archived_sms_sender = create_service_sms_sender(
        service=service, sms_sender="second", is_default=False, archived=True
    )
    with pytest.raises(expected_exception=SQLAlchemyError):
        dao_get_service_sms_senders_by_id(service_id=service.id, service_sms_sender_id=archived_sms_sender.id)


def test_dao_get_sms_senders_by_service_id(notify_db_session):
    service = create_service()
    second_sender = dao_add_sms_sender_for_service(
        service_id=service.id, sms_sender="second", is_default=False, inbound_number_id=None
    )
    results = dao_get_sms_senders_by_service_id(service_id=service.id)
    assert len(results) == 2
    for x in results:
        if x.is_default:
            assert x.sms_sender == "testing"
        else:
            assert x == second_sender


def test_dao_get_sms_senders_by_service_id_does_not_return_archived_senders(notify_db_session):
    service = create_service()
    archived_sms_sender = create_service_sms_sender(
        service=service, sms_sender="second", is_default=False, archived=True
    )
    results = dao_get_sms_senders_by_service_id(service_id=service.id)

    assert len(results) == 1
    assert archived_sms_sender not in results


def test_dao_add_sms_sender_for_service(notify_db_session):
    service = create_service()
    new_sms_sender = dao_add_sms_sender_for_service(
        service_id=service.id, sms_sender="new_sms", is_default=False, inbound_number_id=None
    )

    service_sms_senders = ServiceSmsSender.query.order_by(ServiceSmsSender.created_at).all()
    assert len(service_sms_senders) == 2
    assert service_sms_senders[0].sms_sender == "testing"
    assert service_sms_senders[0].is_default
    assert not service_sms_senders[0].archived
    assert service_sms_senders[1] == new_sms_sender


def test_dao_add_sms_sender_for_service_switches_default(notify_db_session):
    service = create_service()
    new_sms_sender = dao_add_sms_sender_for_service(
        service_id=service.id, sms_sender="new_sms", is_default=True, inbound_number_id=None
    )

    service_sms_senders = ServiceSmsSender.query.order_by(ServiceSmsSender.created_at).all()
    assert len(service_sms_senders) == 2
    assert service_sms_senders[0].sms_sender == "testing"
    assert not service_sms_senders[0].is_default
    assert service_sms_senders[1] == new_sms_sender


def test_dao_update_service_sms_sender(notify_db_session):
    service = create_service()
    service_sms_senders = ServiceSmsSender.query.filter_by(service_id=service.id).all()
    assert len(service_sms_senders) == 1
    sms_sender_to_update = service_sms_senders[0]

    dao_update_service_sms_sender(
        service_id=service.id, service_sms_sender_id=sms_sender_to_update.id, is_default=True, sms_sender="updated"
    )
    sms_senders = ServiceSmsSender.query.filter_by(service_id=service.id).all()
    assert len(sms_senders) == 1
    assert sms_senders[0].is_default
    assert sms_senders[0].sms_sender == "updated"
    assert not sms_senders[0].inbound_number_id


def test_dao_update_service_sms_sender_switches_default(notify_db_session):
    service = create_service()
    sms_sender = dao_add_sms_sender_for_service(
        service_id=service.id, sms_sender="new_sms", is_default=False, inbound_number_id=None
    )
    dao_update_service_sms_sender(
        service_id=service.id, service_sms_sender_id=sms_sender.id, is_default=True, sms_sender="updated"
    )
    sms_senders = ServiceSmsSender.query.filter_by(service_id=service.id).order_by(ServiceSmsSender.created_at).all()
    assert len(sms_senders) == 2
    assert sms_senders[0].sms_sender == "testing"
    assert not sms_senders[0].is_default
    assert sms_senders[1].sms_sender == "updated"
    assert sms_senders[1].is_default


def test_dao_update_service_sms_sender_raises_exception_when_no_default_after_update(notify_db_session):
    service = create_service()
    sms_sender = dao_add_sms_sender_for_service(
        service_id=service.id, sms_sender="new_sms", is_default=True, inbound_number_id=None
    )
    with pytest.raises(expected_exception=Exception) as e:
        dao_update_service_sms_sender(
            service_id=service.id, service_sms_sender_id=sms_sender.id, is_default=False, sms_sender="updated"
        )
    assert "You must have at least one SMS sender as the default" in str(e.value)


def test_update_existing_sms_sender_with_inbound_number(notify_db_session):
    service = create_service()
    inbound_number = create_inbound_number(number="12345", service_id=service.id)

    existing_sms_sender = ServiceSmsSender.query.filter_by(service_id=service.id).one()
    sms_sender = update_existing_sms_sender_with_inbound_number(
        service_sms_sender=existing_sms_sender, sms_sender=inbound_number.number, inbound_number_id=inbound_number.id
    )

    assert sms_sender.inbound_number_id == inbound_number.id
    assert sms_sender.sms_sender == inbound_number.number
    assert sms_sender.is_default


def test_update_existing_sms_sender_with_inbound_number_raises_exception_if_inbound_number_does_not_exist(
    notify_db_session,
):
    service = create_service()
    existing_sms_sender = ServiceSmsSender.query.filter_by(service_id=service.id).one()
    with pytest.raises(expected_exception=SQLAlchemyError):
        update_existing_sms_sender_with_inbound_number(
            service_sms_sender=existing_sms_sender, sms_sender="blah", inbound_number_id=uuid.uuid4()
        )


def test_archive_sms_sender(notify_db_session):
    service = create_service()
    second_sms_sender = dao_add_sms_sender_for_service(service_id=service.id, sms_sender="second", is_default=False)

    archive_sms_sender(service_id=service.id, sms_sender_id=second_sms_sender.id)

    assert second_sms_sender.archived is True
    assert second_sms_sender.updated_at is not None


def test_archive_sms_sender_does_not_archive_a_sender_for_a_different_service(sample_service):
    service = create_service(service_name="First service")
    sms_sender = dao_add_sms_sender_for_service(service_id=sample_service.id, sms_sender="second", is_default=False)

    with pytest.raises(SQLAlchemyError):
        archive_sms_sender(service.id, sms_sender.id)

    assert not sms_sender.archived


def test_archive_sms_sender_raises_an_error_if_attempting_to_archive_a_default(notify_db_session):
    service = create_service()
    sms_sender = service.service_sms_senders[0]

    with pytest.raises(ArchiveValidationError) as e:
        archive_sms_sender(service_id=service.id, sms_sender_id=sms_sender.id)

    assert "You cannot delete a default sms sender" in str(e.value)


@pytest.mark.parametrize("is_default", [True, False])
def test_archive_sms_sender_raises_an_error_if_attempting_to_archive_an_inbound_number(notify_db_session, is_default):
    service = create_service_with_inbound_number(inbound_number="7654321")
    dao_add_sms_sender_for_service(service.id, "second", is_default=True)

    inbound_number = next(x for x in service.service_sms_senders if x.inbound_number_id)

    # regardless of whether inbound number is default or not, can't delete it
    dao_update_service_sms_sender(service.id, inbound_number.id, is_default=is_default)

    with pytest.raises(ArchiveValidationError) as e:
        archive_sms_sender(service_id=service.id, sms_sender_id=inbound_number.id)

    assert "You cannot delete an inbound number" in str(e.value)
    assert not inbound_number.archived
