from datetime import timedelta
from decimal import Decimal

from django.test import TestCase
from django.utils import timezone

from billing.models import Payment, Renewal
from billing.workflows import create_renewal_for_service, mark_payment_as_paid, mark_renewal_as_renewed
from clients.models import Client
from core.choices import BillingCycle, PaymentMethod, ServiceStatus, ServiceType
from services.models import Service


class BillingWorkflowTests(TestCase):
    def setUp(self):
        self.client_record = Client.objects.create(name="Cliente Teste", email="cliente@example.com")
        self.service = Service.objects.create(
            client=self.client_record,
            service_type=ServiceType.HOSTING,
            name="Alojamento Teste",
            status=ServiceStatus.ACTIVE,
            next_renewal_date=timezone.localdate() + timedelta(days=30),
            billing_cycle=BillingCycle.ANNUAL,
            cost_price=Decimal("50.00"),
            sale_price=Decimal("100.00"),
        )

    def test_create_renewal_does_not_duplicate(self):
        first, first_created = create_renewal_for_service(self.service)
        second, second_created = create_renewal_for_service(self.service)
        self.assertTrue(first_created)
        self.assertFalse(second_created)
        self.assertEqual(first.pk, second.pk)
        self.assertEqual(self.service.internal_tasks.count(), 1)

    def test_paid_payment_updates_renewal(self):
        renewal, _ = create_renewal_for_service(self.service)
        payment = Payment.objects.create(
            renewal=renewal,
            amount=Decimal("100.00"),
            method=PaymentMethod.MBWAY,
        )
        mark_payment_as_paid(payment)
        renewal.refresh_from_db()
        self.assertEqual(renewal.status, Renewal.Status.PAID)
        self.assertEqual(renewal.outstanding_amount, Decimal("0.00"))

    def test_mark_renewed_requires_payment(self):
        renewal, _ = create_renewal_for_service(self.service)
        with self.assertRaises(ValueError):
            mark_renewal_as_renewed(renewal)

    def test_mark_renewed_updates_next_date_after_payment(self):
        renewal, _ = create_renewal_for_service(self.service)
        payment = Payment.objects.create(
            renewal=renewal,
            amount=Decimal("100.00"),
            method=PaymentMethod.MBWAY,
        )
        mark_payment_as_paid(payment)
        old_due_date = renewal.due_date
        mark_renewal_as_renewed(renewal)
        renewal.refresh_from_db()
        self.service.refresh_from_db()
        self.assertEqual(renewal.status, Renewal.Status.RENEWED)
        self.assertGreater(self.service.next_renewal_date, old_due_date)
