"""CRM rules. Scope of crm.view / crm.update: `self` = customers I own or created;
`organization` (or wider) = all customers. Deleting and re-owning need organization scope."""
from __future__ import annotations

from django.db.models import OuterRef, Q, Subquery
from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError

from apps.audit import services as audit
from apps.crm.models import Customer, Interaction
from apps.organizations.models import OrganizationMembership
from domain.access_policy import scope_rank


def with_last_contact(qs):
    """Annotate each customer with the date/time of its most recent interaction."""
    latest = Interaction.objects.filter(customer=OuterRef("pk")).order_by("-occurred_on", "-occurred_time", "-id")
    return qs.annotate(
        last_contact_on=Subquery(latest.values("occurred_on")[:1]),
        last_contact_time=Subquery(latest.values("occurred_time")[:1]),
    )


def visible_customers(ctx, permission: str):
    qs = with_last_contact(Customer.objects.filter(organization=ctx.organization).select_related("owner", "created_by"))
    scope = ctx.effective.scope_of(permission)
    if scope is not None and scope_rank(scope) >= scope_rank("organization"):
        return qs
    return qs.filter(Q(owner=ctx.user) | Q(created_by=ctx.user))


def get_customer(ctx, pk: int, permission: str) -> Customer:
    customer = visible_customers(ctx, permission).filter(pk=pk).first()
    if customer is None:
        raise NotFound()
    return customer


def _owner(ctx, user_id):
    member = OrganizationMembership.objects.filter(
        organization=ctx.organization, user_id=user_id, status="active"
    ).select_related("user").first()
    if member is None:
        raise ValidationError({"owner": "The owner is not an active member of this organization."})
    return member.user


def _check(data: dict) -> None:
    if "name" in data and not str(data["name"]).strip():
        raise ValidationError({"name": "Name is required."})
    for field, choices in (("kind", Customer.KINDS), ("status", Customer.STATUSES)):
        if field in data and data[field] not in dict(choices):
            raise ValidationError({field: f"Invalid {field}."})


FIELDS = ("name", "kind", "status", "phone", "email", "address", "notes")


def create_customer(ctx, *, data: dict, request=None) -> Customer:
    _check({**data, "name": data.get("name", "")})
    owner_id = data.get("owner")
    if owner_id is not None and owner_id != ctx.user.pk and not ctx.has("crm.assign", "organization"):
        raise PermissionDenied("You may not assign customers to others.", code="permission_denied")
    customer = Customer.objects.create(
        organization=ctx.organization, created_by=ctx.user,
        owner=_owner(ctx, owner_id) if owner_id is not None else ctx.user,
        **{f: data[f] for f in FIELDS if f in data},
    )
    audit.record(actor=ctx.user, action="customer.create", organization=ctx.organization, entity_type="customer", entity_id=customer.pk, after={"name": customer.name}, request=request)
    return customer


def update_customer(ctx, customer: Customer, *, data: dict, request=None) -> Customer:
    _check(data)
    if "owner" in data and data["owner"] != customer.owner_id:
        if not ctx.has("crm.assign", "organization"):
            raise PermissionDenied("You may not reassign customers.", code="permission_denied")
        customer.owner = _owner(ctx, data["owner"]) if data["owner"] is not None else None
    for field in FIELDS:
        if field in data:
            setattr(customer, field, data[field])
    customer.save()
    audit.record(actor=ctx.user, action="customer.update", organization=ctx.organization, entity_type="customer", entity_id=customer.pk, after={"status": customer.status, "owner": customer.owner_id}, request=request)
    return customer


def delete_customer(ctx, customer: Customer, *, request=None) -> None:
    pk = customer.pk
    customer.delete()
    audit.record(actor=ctx.user, action="customer.delete", organization=ctx.organization, entity_type="customer", entity_id=pk, request=request)


def add_interaction(ctx, customer: Customer, *, data: dict, request=None) -> Interaction:
    if data.get("type") not in dict(Interaction.TYPES):
        raise ValidationError({"type": "Invalid type."})
    if not str(data.get("summary", "")).strip():
        raise ValidationError({"summary": "Summary is required."})
    data = {**data, "occurred_time": data.get("occurred_time")}
    if data.get("follow_up_on") and data["follow_up_on"] < data["occurred_on"]:
        raise ValidationError({"follow_up_on": "Follow-up is before the interaction date."})
    interaction = Interaction.objects.create(organization=ctx.organization, customer=customer, created_by=ctx.user, **data)
    audit.record(actor=ctx.user, action="interaction.create", organization=ctx.organization, entity_type="customer", entity_id=customer.pk, request=request)
    return interaction


def get_interaction(ctx, pk: int) -> Interaction:
    interaction = Interaction.objects.filter(organization=ctx.organization, pk=pk).select_related("customer").first()
    if interaction is None:
        raise NotFound()
    get_customer(ctx, interaction.customer_id, "crm.update")  # same visibility as editing the customer
    return interaction


def assignable_members(ctx):
    return OrganizationMembership.objects.filter(organization=ctx.organization, status="active").select_related("user").order_by("user__name")
