

from .models import DriverProfile, VehicleProfile, QuoteResult

class PremiumCalculator:
    """
    Simple, explainable premium model for motor insurance.

    This is used both in:
    - The Django web app (/quote, /submit)
    - The Lambda validation pipeline (re-rating after claims)
    """

    BASE_RATE = 400.0  # base annual cost in EUR

    LOCATION_FACTORS = {
        "low": 1.00,
        "medium": 1.20,
        "high": 1.45,
    }

    USAGE_FACTORS = {
        "social": 1.00,
        "commute": 1.15,
        "business": 1.35,
    }

    def _age_factor(self, age: int) -> float:
        if age < 21:
            return 1.7
        if 21 <= age <= 24:
            return 1.4
        if 25 <= age <= 30:
            return 1.2
        if 31 <= age <= 60:
            return 1.0
        return 1.3

    def _experience_factor(self, years_licensed: int) -> float:
        if years_licensed >= 8:
            return 0.90
        if years_licensed >= 5:
            return 0.95
        if years_licensed >= 2:
            return 1.00
        return 1.10  # very new drivers

    def _location_factor(self, band: str) -> float:
        return self.LOCATION_FACTORS.get(band, 1.20)

    def _usage_factor(self, usage: str) -> float:
        return self.USAGE_FACTORS.get(usage, 1.15)

    def _claims_factor(self, prior_claims: int) -> float:
        """
        12% loading per prior claim.
        """
        return 1.00 + (0.12 * max(prior_claims, 0))

    def _no_claims_factor(self, no_claims_years: int) -> float:
        """
        Discount of 3% per NCD year, capped at 25% discount.
        """
        discount = min(no_claims_years * 0.03, 0.25)
        return 1.00 - discount

    def calculate(self, driver: DriverProfile, vehicle: VehicleProfile) -> QuoteResult:
        """
        Main entry point.

        Returns a QuoteResult with:
        - base_premium: fixed base before loadings
        - final_premium: total after applying all factors
        - risk_score: 0–1 (higher = riskier)
        - breakdown: per-factor contributions
        """
        base = self.BASE_RATE

        age_factor = self._age_factor(driver.age)
        exp_factor = self._experience_factor(driver.years_licensed)
        loc_factor = self._location_factor(driver.location_band)
        use_factor = self._usage_factor(vehicle.usage)
        claims_factor = self._claims_factor(driver.prior_claims)
        ncd_factor = self._no_claims_factor(driver.no_claims_years)

        # Compute final premium
        final_premium = (
            base
            * age_factor
            * exp_factor
            * loc_factor
            * use_factor
            * claims_factor
            * ncd_factor
        )

        # Crude 0–1 risk score based on key multipliers
        raw = (age_factor + loc_factor + use_factor + claims_factor) / 4.0
        # normalise: assume 1.0 is "average", clamp
        risk_score = max(0.0, min(1.0, round(raw / 2.0, 3)))

        breakdown = {
            "age_factor": age_factor,
            "experience_factor": exp_factor,
            "location_factor": loc_factor,
            "usage_factor": use_factor,
            "prior_claims_factor": claims_factor,
            "no_claims_factor": ncd_factor,
        }

        return QuoteResult(
            base_premium=round(base, 2),
            final_premium=round(final_premium, 2),
            risk_score=risk_score,
            breakdown=breakdown,
        )


if __name__ == "__main__":
    # Small self-test, like in the tutorial
    driver = DriverProfile(
        age=26,
        years_licensed=5,
        location_band="medium",
        no_claims_years=3,
        prior_claims=1,
    )
    vehicle = VehicleProfile(
        value=15000.0,
        annual_mileage=18000,
        usage="commute",
    )
    calc = PremiumCalculator()
    quote = calc.calculate(driver, vehicle)
    print("Base premium:", quote.base_premium)
    print("Final premium:", quote.final_premium)
    print("Risk score:", quote.risk_score)
    print("Breakdown:", quote.breakdown)
