"""Business rules for the CRM. Views call these; nothing here knows about HTTP."""
from django.db import transaction
from django.utils import timezone

from schools.access import crm_can_assign

from .constants import (ATTEMPT_ONLY_OUTCOMES, CONTACT_TYPES, PRIORITY_LABELS, STAGE_LABELS,
                        TERMINAL)
from .models import Activity, Lead


class RuleError(Exception):
    """A change the user is not allowed to make (shown to them as a message)."""


def get_lead(school):
    lead, _ = Lead.objects.select_related("owner").get_or_create(school=school)
    return lead


def check_owner_change(user, lead, new_owner):
    if new_owner == lead.owner or crm_can_assign(user):
        return
    if new_owner not in (None, user):
        raise RuleError("You can only assign a lead to yourself. Ask a manager to assign it to someone else.")
    if lead.owner not in (None, user):
        raise RuleError("This lead is assigned to someone else. Ask a manager to reassign it.")


def _name(u):
    return u.get_username() if u else "nobody"


def _priority_label(value):
    return PRIORITY_LABELS.get(value or "", "-")


def _display_change(value):
    if value in (None, ""):
        return "-"
    if hasattr(value, "isoformat"):
        return value.isoformat()
    return str(value)


@transaction.atomic
def update_lead(lead, user, data):
    """Apply the lead form. Returns the list of changes (also written to the timeline)."""
    check_owner_change(user, lead, data["owner"])
    changes, stage_changed = [], False
    terminal_stage = data["stage"] in TERMINAL
    next_followup = None if terminal_stage else data["next_followup"]
    next_action = "" if terminal_stage else data["next_action"]
    lost_reason = data["lost_reason"] if data["stage"] == "lost" else ""

    def record_change(label, old_value, new_value):
        if old_value != new_value:
            changes.append(f"{label}: {_display_change(old_value)} → {_display_change(new_value)}")

    if data["stage"] != lead.stage:
        changes.append(f"Stage: {STAGE_LABELS[lead.stage]} → {STAGE_LABELS[data['stage']]}")
        lead.stage, lead.stage_changed_at, stage_changed = data["stage"], timezone.now(), True
    if data["priority"] != lead.priority:
        changes.append(f"Priority: {_priority_label(lead.priority)} → {_priority_label(data['priority'])}")
        lead.priority = data["priority"]
    if data["owner"] != lead.owner:
        changes.append(f"Owner: {_name(lead.owner)} → {_name(data['owner'])}")
        lead.owner = data["owner"]
    record_change("Next follow-up", lead.next_followup, next_followup)
    record_change("What to do next", lead.next_action, next_action)
    record_change("Deal value", lead.deal_value, data["deal_value"])
    record_change("Lost reason", lead.lost_reason, lost_reason)
    lead.next_followup = next_followup
    lead.next_action = next_action
    lead.deal_value = data["deal_value"]
    lead.lost_reason = lost_reason
    lead.save()
    if changes:
        extra = f" (reason: {lead.lost_reason})" if stage_changed and lead.stage == "lost" else ""
        Activity.objects.create(lead=lead, kind="stage" if stage_changed else "system",
                                notes="; ".join(changes) + extra, created_by=user)
    return changes


@transaction.atomic
def log_activity(lead, user, kind, outcome, notes, occurred_at, followup_date=None, followup_action=""):
    act = Activity.objects.create(lead=lead, kind=kind, outcome=outcome or "", notes=notes or "",
                                  occurred_at=occurred_at, created_by=user)
    today = timezone.localdate()
    if kind in CONTACT_TYPES:
        lead.attempts += 1
        reached = outcome not in ATTEMPT_ONLY_OUTCOMES
        if reached:
            if not lead.last_contacted_at or occurred_at > lead.last_contacted_at:
                lead.last_contacted_at = occurred_at
            if lead.stage == "new":
                lead.stage, lead.stage_changed_at = "contacted", timezone.now()
                Activity.objects.create(lead=lead, kind="stage", created_by=user,
                                        notes="Stage: New → Contacted (automatic)")
            if lead.next_followup and lead.next_followup <= today and not followup_date:
                lead.next_followup, lead.next_action = None, ""      # that follow-up is now done
    if followup_date:
        lead.next_followup = followup_date
        if followup_action:
            lead.next_action = followup_action
    lead.save()
    return act
