import pytest
from fastapi import HTTPException

from api.v1.modules.crm.accounts import service as accounts_service
from api.v1.modules.crm.accounts.dto import AccountCreate
from api.v1.modules.crm.contacts import service as contacts_service
from api.v1.modules.crm.contacts.dto import ContactCreate
from api.v1.modules.crm.opportunities import service as opp_service
from api.v1.modules.crm.opportunities.dto import OpportunityCreate
from api.v1.modules.crm.service_requests import service
from api.v1.modules.crm.service_requests.dto import (
    RateRequestCreate,
    ServiceRequestCreate,
    ServiceRequestFromOpportunityInput,
    ServiceRequestUpdate,
)

T, C = 1, 1


def test_create_service_request(db):
    acc = accounts_service.create_account(db, AccountCreate(name="Cliente"), T, C)
    sr = service.create_service_request(
        db,
        ServiceRequestCreate(
            account_id=acc.id, operation_type="exportacion", transport_mode="maritimo",
            service_type="puerta_puerta", origin="Manzanillo", destination="Long Beach",
            incoterm="FOB", load_type="FCL",
        ),
        T, C, user_id="dev",
    )
    assert sr.id is not None
    assert sr.status == "nueva"
    assert sr.created_by == "dev"


def test_service_request_rejects_unknown_account(db):
    with pytest.raises(HTTPException) as exc:
        service.create_service_request(db, ServiceRequestCreate(account_id=999, operation_type="importacion"), T, C)
    assert exc.value.status_code == 422


def test_filter_by_operation_and_status(db):
    service.create_service_request(db, ServiceRequestCreate(operation_type="exportacion"), T, C)
    service.create_service_request(db, ServiceRequestCreate(operation_type="importacion"), T, C)
    exp = service.get_service_requests(db, T, C, operation_type="exportacion")
    assert len(exp) == 1 and exp[0].operation_type == "exportacion"


def test_rate_request_requires_service_request(db):
    with pytest.raises(HTTPException) as exc:
        service.create_rate_request(
            db, RateRequestCreate(service_request_id=999, concept="flete_internacional"), T, C
        )
    assert exc.value.status_code == 404


def test_rate_request_ok_and_listed(db):
    sr = service.create_service_request(db, ServiceRequestCreate(operation_type="exportacion"), T, C)
    service.create_rate_request(
        db, RateRequestCreate(service_request_id=sr.id, concept="flete_internacional", rate_amount=1200, currency="USD"),
        T, C,
    )
    rates = service.get_rate_requests(db, T, C, service_request_id=sr.id)
    assert len(rates) == 1 and rates[0].concept == "flete_internacional"


def test_update_service_request_status(db):
    sr = service.create_service_request(db, ServiceRequestCreate(operation_type="exportacion"), T, C)
    upd = service.update_service_request(db, sr.id, ServiceRequestUpdate(status="en_analisis"), T, C)
    assert upd.status == "en_analisis"


# ----- Campos del documento maestro de cotización -----

def test_create_service_request_new_fields(db):
    sr = service.create_service_request(
        db,
        ServiceRequestCreate(
            operation_type="importacion", load_type="LCL", priority="alta",
            origin_country="CHN", origin_city="Shanghai",
            destination_country="MEX", destination_city="Manzanillo",
            cargo_value=15000, insurance_required=True, hazardous_imo=True,
            pieces_count=12, net_weight=800, measurement_unit="kg",
            additional_services=["seguro", "despacho_aduanal"],
            payment_method="99", client_notes="Manejo con cuidado",
        ),
        T, C,
    )
    assert sr.origin_country == "CHN"
    assert sr.insurance_required is True
    assert sr.hazardous_imo is True
    assert sr.additional_services == ["seguro", "despacho_aduanal"]
    assert sr.pieces_count == 12


def test_service_request_rejects_unknown_contact(db):
    with pytest.raises(HTTPException) as exc:
        service.create_service_request(
            db, ServiceRequestCreate(operation_type="importacion", contact_id=999), T, C
        )
    assert exc.value.status_code == 422


def test_service_request_generates_folio(db):
    sr = service.create_service_request(db, ServiceRequestCreate(operation_type="exportacion"), T, C)
    assert sr.reference is not None
    assert sr.reference.startswith("S")
    assert sr.reference.endswith("-E")


def test_service_request_accepts_ambas(db):
    sr = service.create_service_request(
        db, ServiceRequestCreate(operation_type="exportacion", load_type="AMBAS"), T, C
    )
    assert sr.load_type == "AMBAS"


def test_from_opportunity_inherits_operation_type_and_backlink(db):
    acc = accounts_service.create_account(db, AccountCreate(name="Cliente"), T, C)
    contact = contacts_service.create_contact(
        db, ContactCreate(account_id=acc.id, first_name="Ana"), T, C
    )
    opp = opp_service.create_opportunity(
        db,
        OpportunityCreate(name="Negocio", account_id=acc.id, contact_id=contact.id,
                          operation_type="importacion"),
        T, C,
    )
    sr = service.create_from_opportunity(
        db, opp.id, ServiceRequestFromOpportunityInput(transport_mode="aereo"), T, C, user_id="dev"
    )
    # Hereda dirección y contacto de la oportunidad
    assert sr.operation_type == "importacion"
    assert sr.contact_id == contact.id
    assert sr.opportunity_id == opp.id
    assert sr.reference.startswith("S") and sr.reference.endswith("-I")
    # Back-link en la oportunidad
    refreshed = opp_service.get_opportunity(db, opp.id, T, C)
    assert refreshed.converted_service_request_id == sr.id


def test_from_opportunity_idempotent(db):
    opp = opp_service.create_opportunity(
        db, OpportunityCreate(name="Negocio", operation_type="exportacion"), T, C
    )
    first = service.create_from_opportunity(db, opp.id, ServiceRequestFromOpportunityInput(), T, C)
    second = service.create_from_opportunity(db, opp.id, ServiceRequestFromOpportunityInput(), T, C)
    assert first.id == second.id  # no crea una segunda solicitud


def test_from_opportunity_without_direction_fails(db):
    opp = opp_service.create_opportunity(db, OpportunityCreate(name="Sin dirección"), T, C)
    with pytest.raises(HTTPException) as exc:
        service.create_from_opportunity(db, opp.id, ServiceRequestFromOpportunityInput(), T, C)
    assert exc.value.status_code == 422
