from datetime import date
from decimal import Decimal

import pytest
from fastapi import HTTPException

from api.v1.modules.crm.quotes import service
from api.v1.modules.crm.quotes.dto import QuoteCreate, QuoteItemCreate, QuoteItemUpdate
from api.v1.modules.crm.service_requests import service as sr_service
from api.v1.modules.crm.service_requests.dto import RateRequestCreate, ServiceRequestCreate

T, C = 1, 1


def test_quote_totals_recompute_on_items(db):
    q = service.create_quote(db, QuoteCreate(reference="COT-001", currency="USD"), T, C)
    service.create_quote_item(
        db, QuoteItemCreate(quote_id=q.id, concept="flete_internacional", quantity=2, unit_cost=100, unit_sale=150), T, C
    )
    service.create_quote_item(
        db, QuoteItemCreate(quote_id=q.id, concept="despacho_aduanal", quantity=1, unit_cost=50, unit_sale=90), T, C
    )
    q = service.get_quote(db, q.id, T, C)
    assert float(q.total_cost) == 250.0  # 2*100 + 1*50
    assert float(q.total_sale) == 390.0  # 2*150 + 1*90


def test_quote_totals_update_and_delete_item(db):
    q = service.create_quote(db, QuoteCreate(reference="COT-002"), T, C)
    item = service.create_quote_item(
        db, QuoteItemCreate(quote_id=q.id, concept="otros", quantity=1, unit_cost=100, unit_sale=200), T, C
    )
    service.update_quote_item(db, item.id, QuoteItemUpdate(unit_sale=Decimal("300")), T, C)
    q = service.get_quote(db, q.id, T, C)
    assert float(q.total_sale) == 300.0
    service.delete_quote_item(db, item.id, T, C)
    q = service.get_quote(db, q.id, T, C)
    assert float(q.total_sale) == 0.0


def test_accept_quote_updates_service_request(db):
    sr = sr_service.create_service_request(db, ServiceRequestCreate(operation_type="exportacion"), T, C)
    q = service.create_quote(db, QuoteCreate(reference="COT-003", service_request_id=sr.id), T, C)
    service.send_quote(db, q.id, T, C)
    accepted = service.accept_quote(db, q.id, T, C)
    assert accepted.status == "aceptada"
    assert accepted.accepted_at is not None
    # la solicitud asociada queda aceptada
    sr = sr_service.get_service_request(db, sr.id, T, C)
    assert sr.status == "aceptada"


# ----- Solicitud → Cotización -----

def _sr_with_rates(db, load_type="FCL"):
    sr = sr_service.create_service_request(
        db, ServiceRequestCreate(operation_type="importacion", load_type=load_type, currency="USD"), T, C
    )
    sr_service.create_rate_request(
        db, RateRequestCreate(service_request_id=sr.id, concept="flete_internacional",
                              rate_amount=1200, currency="USD"), T, C
    )
    sr_service.create_rate_request(
        db, RateRequestCreate(service_request_id=sr.id, concept="despacho_aduanal",
                              rate_amount=300, currency="USD"), T, C
    )
    return sr


def test_quote_from_service_request_seeds_items(db):
    sr = _sr_with_rates(db)
    quotes = service.create_quotes_from_service_request(db, sr.id, T, C, user_id="dev")
    assert len(quotes) == 1
    q = quotes[0]
    assert q.service_request_id == sr.id
    assert q.reference.startswith("C") and q.reference.endswith("-I")
    items = service.get_quote_items(db, q.id, T, C)
    assert len(items) == 2
    assert float(q.total_sale) == 1500.0  # 1200 + 300


def test_quote_from_service_request_without_rates(db):
    sr = sr_service.create_service_request(
        db, ServiceRequestCreate(operation_type="exportacion", load_type="FCL"), T, C
    )
    quotes = service.create_quotes_from_service_request(db, sr.id, T, C)
    assert len(quotes) == 1
    assert service.get_quote_items(db, quotes[0].id, T, C) == []


def test_quote_from_service_request_not_found(db):
    with pytest.raises(HTTPException) as exc:
        service.create_quotes_from_service_request(db, 999, T, C)
    assert exc.value.status_code == 404


def test_quote_from_service_request_ambas_genera_dos(db):
    sr = _sr_with_rates(db, load_type="AMBAS")
    quotes = service.create_quotes_from_service_request(db, sr.id, T, C)
    assert len(quotes) == 2
    variants = {q.load_type for q in quotes}
    assert variants == {"FCL", "LCL"}
    # cada variante siembra sus propios conceptos y toma su propio folio
    assert quotes[0].reference != quotes[1].reference
    for q in quotes:
        assert len(service.get_quote_items(db, q.id, T, C)) == 2


def test_quote_from_service_request_seeds_additional_services(db):
    sr = sr_service.create_service_request(
        db,
        ServiceRequestCreate(
            operation_type="importacion", load_type="FCL", currency="USD",
            additional_services=["seguro", "despacho_aduanal"],
            additional_service_costs={"seguro": 500, "despacho_aduanal": 300},
        ),
        T, C,
    )
    quotes = service.create_quotes_from_service_request(db, sr.id, T, C)
    items = service.get_quote_items(db, quotes[0].id, T, C)
    costs = {i.concept: float(i.unit_cost) for i in items}
    assert costs.get("seguro") == 500.0
    assert costs.get("despacho_aduanal") == 300.0
    # el costo estimado de la solicitud es el punto de partida (costo=venta)
    assert float(quotes[0].total_sale) == 800.0


def test_quote_from_service_request_aereo_seeds_pvol_concept(db):
    sr = sr_service.create_service_request(
        db,
        ServiceRequestCreate(
            operation_type="exportacion", load_type="AEREO", currency="USD",
            weight=200, length_cm=120, width_cm=120, height_cm=100, pallets_count=3,
        ),
        T, C,
    )
    quotes = service.create_quotes_from_service_request(db, sr.id, T, C)
    assert len(quotes) == 1
    assert quotes[0].load_type == "AEREO"
    flete = [i for i in service.get_quote_items(db, quotes[0].id, T, C) if i.concept == "flete_internacional"]
    assert len(flete) == 1
    # cantidad del flete = peso a cobrar (P/Vol 720 > bruto 200)
    assert float(flete[0].quantity) == 720.0


def test_quote_from_service_request_sets_issue_date_today(db):
    sr = _sr_with_rates(db)
    quotes = service.create_quotes_from_service_request(db, sr.id, T, C)
    assert quotes[0].issue_date == date.today()


def test_create_quote_sets_issue_date_today(db):
    q = service.create_quote(db, QuoteCreate(reference="COT-DATE"), T, C)
    assert q.issue_date == date.today()
