"""CRM services."""

from __future__ import annotations

from dataclasses import dataclass, field
from decimal import Decimal

from .entities import Activity, Lead, Stage
from .enums import LeadPriority, LeadType
from .exceptions import InvalidLeadError
from .policies import DefaultLeadStagePolicy, LeadStagePolicy
from .value_objects import LeadScore


@dataclass(frozen=True)
class LeadScoringService:
    base_contact_score: int = 20
    email_bonus: int = 15
    phone_bonus: int = 10
    company_bonus: int = 10
    activity_bonus: int = 10
    opportunity_bonus: int = 10
    revenue_threshold: Decimal = Decimal("1000000")
    revenue_bonus: int = 15

    def score(self, lead: Lead) -> LeadScore:
        if not isinstance(lead, Lead):
            raise TypeError("lead must be a Lead instance")
        score = 0
        if lead.contact is not None:
            score += self.base_contact_score
            if lead.contact.email is not None:
                score += self.email_bonus
            if lead.contact.phone is not None:
                score += self.phone_bonus
            if lead.contact.company:
                score += self.company_bonus
        if lead.expected_revenue >= self.revenue_threshold:
            score += self.revenue_bonus
        elif lead.expected_revenue > 0:
            score += max(5, min(self.revenue_bonus, int(lead.expected_revenue / Decimal("250000"))))
        score += min(self.activity_bonus, 5 * len(lead.open_activities))
        if lead.lead_type == LeadType.OPPORTUNITY:
            score += self.opportunity_bonus
        if lead.priority in {LeadPriority.HIGH, LeadPriority.VERY_HIGH}:
            score += 5
        score += int(lead.stage.probability * Decimal("20"))
        return LeadScore(min(100, score))

    def update(self, lead: Lead) -> LeadScore:
        score = self.score(lead)
        lead.score = score
        return score


@dataclass
class LeadWorkflowService:
    stage_policy: LeadStagePolicy = field(default_factory=DefaultLeadStagePolicy)
    scoring_service: LeadScoringService = field(default_factory=LeadScoringService)

    def move(self, lead: Lead, target_stage: Stage) -> Lead:
        self._validate_lead(lead)
        lead.set_stage(target_stage, policy=self.stage_policy)
        self.scoring_service.update(lead)
        return lead

    def qualify(self, lead: Lead, target_stage: Stage) -> Lead:
        self._validate_lead(lead)
        lead.qualify(target_stage, policy=self.stage_policy)
        self.scoring_service.update(lead)
        return lead

    def mark_won(self, lead: Lead, target_stage: Stage) -> Lead:
        self._validate_lead(lead)
        lead.mark_won(target_stage, policy=self.stage_policy)
        self.scoring_service.update(lead)
        return lead

    def mark_lost(self, lead: Lead, reason: str, target_stage: Stage) -> Lead:
        self._validate_lead(lead)
        lead.mark_lost(reason, target_stage, policy=self.stage_policy)
        self.scoring_service.update(lead)
        return lead

    def add_activity(self, lead: Lead, activity: Activity) -> Activity:
        self._validate_lead(lead)
        created = lead.add_activity(activity)
        self.scoring_service.update(lead)
        return created

    def complete_activity(self, lead: Lead, activity: Activity) -> None:
        self._validate_lead(lead)
        lead.complete_activity(activity)
        self.scoring_service.update(lead)

    def _validate_lead(self, lead: Lead) -> None:
        if not isinstance(lead, Lead):
            raise InvalidLeadError("lead must be a Lead instance")
