# app/modules/prospecting/services/scoring_service.py
"""
Opportunity scoring service.

Migrated from marketing-.lu-domains/app/services/scoring_service.py.
Scores prospects on a 0-100 scale across 4 categories:
- Technical Health (max 40pts)
- Modernity (max 25pts)
- Business Value (max 25pts)
- Engagement (max 10pts)

Extended for offline leads with additional scoring factors.
"""

import json
import logging

from sqlalchemy.orm import Session

from app.modules.prospecting.models import (
    Prospect,
    ProspectChannel,
    ProspectScore,
)

logger = logging.getLogger(__name__)

# Outdated CMS list
OUTDATED_CMS = {"drupal", "joomla", "typo3"}


class ScoringService:
    """Service for computing opportunity scores."""

    def compute_score(self, db: Session, prospect: Prospect) -> ProspectScore:
        """Compute or update the opportunity score for a prospect."""
        tech_health = 0
        modernity = 0
        business_value = 0
        engagement = 0
        reason_flags = []
        breakdown = {}

        if prospect.channel == ProspectChannel.OFFLINE:
            # Offline lead scoring
            tech_health, modernity, business_value, engagement, reason_flags, breakdown = (
                self._score_offline(prospect)
            )
        else:
            # Digital lead scoring
            tech_health, modernity, business_value, engagement, reason_flags, breakdown = (
                self._score_digital(prospect)
            )

        total = min(tech_health + modernity + business_value + engagement, 100)

        # Determine lead tier
        if total >= 70:
            lead_tier = "top_priority"
        elif total >= 50:
            lead_tier = "quick_win"
        elif total >= 30:
            lead_tier = "strategic"
        else:
            lead_tier = "low_priority"

        # Upsert score
        score = prospect.score
        if not score:
            score = ProspectScore(prospect_id=prospect.id)
            db.add(score)

        score.score = total
        score.technical_health_score = tech_health
        score.modernity_score = modernity
        score.business_value_score = business_value
        score.engagement_score = engagement
        score.reason_flags = json.dumps(reason_flags)
        score.score_breakdown = json.dumps(breakdown)
        score.lead_tier = lead_tier

        db.flush()
        logger.info("Scored prospect %d: %d (%s)", prospect.id, total, lead_tier)
        return score

    def compute_all(self, db: Session, limit: int | None = None) -> int:
        """Compute scores for all prospects. Returns count of scored prospects."""
        query = db.query(Prospect)
        if limit:
            query = query.limit(limit)

        count = 0
        for prospect in query.all():
            self.compute_score(db, prospect)
            count += 1

        return count

    def _score_digital(self, prospect: Prospect) -> tuple:
        """Score a digital (domain-scanned) prospect."""
        tech_health = 0
        modernity = 0
        business_value = 0
        engagement = 0
        flags = []
        breakdown = {
            "technical_health": {},
            "modernity": {},
            "business_value": {},
            "engagement": {},
        }

        # === TECHNICAL HEALTH (max 40) ===
        if not prospect.uses_https:
            tech_health += 15
            flags.append("no_ssl")
            breakdown["technical_health"]["no_ssl"] = 15

        perf = prospect.performance_profile
        if perf and perf.performance_score is not None:
            if perf.performance_score < 30:
                tech_health += 15
                flags.append("very_slow")
                breakdown["technical_health"]["very_slow"] = 15
            elif perf.performance_score < 50:
                tech_health += 10
                flags.append("slow")
                breakdown["technical_health"]["slow"] = 10
            elif perf.performance_score < 70:
                tech_health += 5
                flags.append("moderate_speed")
                breakdown["technical_health"]["moderate_speed"] = 5

            if perf.is_mobile_friendly is False:
                tech_health += 10
                flags.append("not_mobile_friendly")
                breakdown["technical_health"]["not_mobile_friendly"] = 10

        tech_health = min(tech_health, 40)

        # === MODERNITY (max 25) ===
        tp = prospect.tech_profile
        if tp:
            if tp.cms and tp.cms.lower() in OUTDATED_CMS:
                modernity += 15
                flags.append("outdated_cms")
                breakdown["modernity"]["outdated_cms"] = 15
            elif tp.cms is None and prospect.has_website:
                modernity += 5
                flags.append("unknown_cms")
                breakdown["modernity"]["unknown_cms"] = 5

            if tp.js_framework and tp.js_framework.lower() == "jquery":
                modernity += 5
                flags.append("legacy_js")
                breakdown["modernity"]["legacy_js"] = 5

            if not tp.analytics:
                modernity += 5
                flags.append("no_analytics")
                breakdown["modernity"]["no_analytics"] = 5

        modernity = min(modernity, 25)

        # === BUSINESS VALUE (max 25) ===
        if prospect.has_website:
            business_value += 10
            breakdown["business_value"]["has_website"] = 10

        if tp and tp.ecommerce_platform:
            business_value += 10
            breakdown["business_value"]["has_ecommerce"] = 10

        if prospect.domain_name and len(prospect.domain_name) <= 15:
            business_value += 5
            breakdown["business_value"]["short_domain"] = 5

        business_value = min(business_value, 25)

        # === ENGAGEMENT (max 10) ===
        contacts = prospect.contacts or []
        if contacts:
            engagement += 5
            flags.append("has_contacts")
            breakdown["engagement"]["has_contacts"] = 5

        has_email = any(c.contact_type == "email" for c in contacts)
        has_phone = any(c.contact_type == "phone" for c in contacts)

        if has_email:
            engagement += 3
            flags.append("has_email")
            breakdown["engagement"]["has_email"] = 3
        if has_phone:
            engagement += 2
            flags.append("has_phone")
            breakdown["engagement"]["has_phone"] = 2

        engagement = min(engagement, 10)

        return tech_health, modernity, business_value, engagement, flags, breakdown

    def _score_offline(self, prospect: Prospect) -> tuple:
        """Score an offline (manually captured) prospect."""
        tech_health = 0
        modernity = 0
        business_value = 0
        engagement = 0
        flags = []
        breakdown = {
            "technical_health": {},
            "modernity": {},
            "business_value": {},
            "engagement": {},
        }

        # Offline prospects without a website are high opportunity
        if not prospect.has_website:
            tech_health = 30
            modernity = 20
            business_value = 20
            flags.extend(["no_website"])
            breakdown["technical_health"]["no_website"] = 30
            breakdown["modernity"]["no_website"] = 20
            breakdown["business_value"]["no_website"] = 20

        # Check for gmail usage (from contacts)
        contacts = prospect.contacts or []
        has_gmail = any(
            c.contact_type == "email" and "@gmail." in c.value.lower()
            for c in contacts
        )
        if has_gmail:
            modernity += 10
            flags.append("uses_gmail")
            breakdown["modernity"]["uses_gmail"] = 10

        modernity = min(modernity, 25)

        # Engagement - offline leads met in person are warm
        if prospect.source in ("street", "networking_event", "referral"):
            engagement += 5
            flags.append("met_in_person")
            breakdown["engagement"]["met_in_person"] = 5

        if contacts:
            has_email = any(c.contact_type == "email" for c in contacts)
            has_phone = any(c.contact_type == "phone" for c in contacts)
            if has_email:
                engagement += 3
                flags.append("has_email")
                breakdown["engagement"]["has_email"] = 3
            if has_phone:
                engagement += 2
                flags.append("has_phone")
                breakdown["engagement"]["has_phone"] = 2

        engagement = min(engagement, 10)

        return tech_health, modernity, business_value, engagement, flags, breakdown


scoring_service = ScoringService()
