"""CRM selectors — read-only queries for contacts, tags, pipeline, leads, opportunities, activities."""

from __future__ import annotations

from django.db.models import Q, QuerySet

from simorgh.apps.crm.models import (
    Contact,
    ContactTag,
    CRMActivity,
    Lead,
    Opportunity,
    Pipeline,
    PipelineStage,
)


def contacts_for_tenant(tenant_id: int) -> QuerySet[Contact]:
    """All non-deleted contacts for a tenant."""
    return Contact.objects.filter(tenant_id=tenant_id)


def contacts_by_type(tenant_id: int, contact_type: str) -> QuerySet[Contact]:
    """Contacts filtered by type (person/company/lead)."""
    return contacts_for_tenant(tenant_id).filter(type=contact_type)


def contacts_search(tenant_id: int, query: str) -> QuerySet[Contact]:
    """Quick full-text search across name, phones, and emails."""
    qs = contacts_for_tenant(tenant_id)
    if not query:
        return qs
    return qs.filter(
        Q(name__icontains=query)
        | Q(phones__number__icontains=query)
        | Q(emails__address__icontains=query)
    ).distinct()


def contacts_with_tags(tenant_id: int, tag_ids: list[int]) -> QuerySet[Contact]:
    """Contacts that have ALL specified tags."""
    qs = contacts_for_tenant(tenant_id)
    for tag_id in tag_ids:
        qs = qs.filter(tags__id=tag_id)
    return qs.distinct()


def contact_by_id(tenant_id: int, pk: int) -> Contact:
    """Fetch a single contact or raise DoesNotExist."""
    return Contact.objects.get(tenant_id=tenant_id, pk=pk, deleted_at__isnull=True)


def contact_by_public_id(tenant_id: int, public_id: str) -> Contact:
    """Fetch a single contact by UUID or raise DoesNotExist."""
    return Contact.objects.get(tenant_id=tenant_id, public_id=public_id, deleted_at__isnull=True)


def tags_for_tenant(tenant_id: int) -> QuerySet[ContactTag]:
    """All tags for a tenant."""
    return ContactTag.objects.filter(tenant_id=tenant_id).order_by("name")


# ===========================================================================
# Pipeline selectors  (S1.2)
# ===========================================================================

def pipelines_for_tenant(tenant_id: int) -> QuerySet[Pipeline]:
    """All pipelines for a tenant, ordered by name."""
    return Pipeline.objects.filter(tenant_id=tenant_id).order_by("name")


def pipeline_by_public_id(tenant_id: int, public_id: str) -> Pipeline:
    """Fetch a single pipeline or raise DoesNotExist."""
    return Pipeline.objects.get(tenant_id=tenant_id, public_id=public_id)


def stages_for_pipeline(pipeline_id: int) -> QuerySet[PipelineStage]:
    """All stages of a pipeline ordered by ``order``."""
    return PipelineStage.objects.filter(pipeline_id=pipeline_id).order_by("order")


def stage_by_public_id(public_id: str) -> PipelineStage:
    """Fetch a single stage or raise DoesNotExist."""
    return PipelineStage.objects.select_related("pipeline").get(public_id=public_id)


# ===========================================================================
# Lead selectors
# ===========================================================================

def leads_for_tenant(
    tenant_id: int,
    *,
    status: str | None = None,
    source: str | None = None,
    owner_id: int | None = None,
    contact_id: int | None = None,
    q: str | None = None,
) -> QuerySet[Lead]:
    """Filtered list of non-deleted leads for a tenant."""
    qs = Lead.objects.filter(tenant_id=tenant_id)
    if status:
        qs = qs.filter(status=status)
    if source:
        qs = qs.filter(source=source)
    if owner_id:
        qs = qs.filter(owner_id=owner_id)
    if contact_id:
        qs = qs.filter(contact_id=contact_id)
    if q:
        qs = qs.filter(Q(title__icontains=q) | Q(notes__icontains=q))
    return qs.order_by("-created_at")


def lead_by_public_id(tenant_id: int, public_id: str) -> Lead:
    """Fetch a single non-deleted lead or raise DoesNotExist."""
    return Lead.objects.get(tenant_id=tenant_id, public_id=public_id, deleted_at__isnull=True)


# ===========================================================================
# Opportunity selectors
# ===========================================================================

def opportunities_for_tenant(
    tenant_id: int,
    *,
    stage_id: int | None = None,
    owner_id: int | None = None,
    status: str | None = None,
    contact_id: int | None = None,
    pipeline_id: int | None = None,
    q: str | None = None,
) -> QuerySet[Opportunity]:
    """Filtered list of non-deleted opportunities for a tenant."""
    qs = Opportunity.objects.filter(tenant_id=tenant_id).select_related("pipeline_stage__pipeline")
    if stage_id:
        qs = qs.filter(pipeline_stage_id=stage_id)
    if pipeline_id:
        qs = qs.filter(pipeline_stage__pipeline_id=pipeline_id)
    if owner_id:
        qs = qs.filter(owner_id=owner_id)
    if status:
        qs = qs.filter(status=status)
    if contact_id:
        qs = qs.filter(contact_id=contact_id)
    if q:
        qs = qs.filter(Q(title__icontains=q))
    return qs.order_by("-created_at")


def opportunity_by_public_id(tenant_id: int, public_id: str) -> Opportunity:
    """Fetch a single non-deleted opportunity or raise DoesNotExist."""
    return Opportunity.objects.select_related("pipeline_stage__pipeline").get(
        tenant_id=tenant_id, public_id=public_id, deleted_at__isnull=True
    )


# ===========================================================================
# CRM Activity selectors
# ===========================================================================

def activities_for_tenant(
    tenant_id: int,
    *,
    type: str | None = None,
    owner_id: int | None = None,
    is_done: bool | None = None,
    linked_opp_id: int | None = None,
    linked_lead_id: int | None = None,
    due_from=None,
    due_to=None,
) -> QuerySet[CRMActivity]:
    """Filtered list of CRM activities for a tenant."""
    qs = CRMActivity.objects.filter(tenant_id=tenant_id)
    if type:
        qs = qs.filter(type=type)
    if owner_id is not None:
        qs = qs.filter(owner_id=owner_id)
    if is_done is not None:
        qs = qs.filter(is_done=is_done)
    if linked_opp_id is not None:
        qs = qs.filter(opportunity_id=linked_opp_id)
    if linked_lead_id is not None:
        qs = qs.filter(lead_id=linked_lead_id)
    if due_from:
        qs = qs.filter(due_at__gte=due_from)
    if due_to:
        qs = qs.filter(due_at__lte=due_to)
    return qs.order_by("-created_at")


def activity_by_public_id(tenant_id: int, public_id: str) -> CRMActivity:
    """Fetch a single CRM activity or raise DoesNotExist."""
    return CRMActivity.objects.get(tenant_id=tenant_id, public_id=public_id)



def contacts_for_tenant(tenant_id: int) -> QuerySet[Contact]:
    """All non-deleted contacts for a tenant."""
    return Contact.objects.filter(tenant_id=tenant_id)


def contacts_by_type(tenant_id: int, contact_type: str) -> QuerySet[Contact]:
    """Contacts filtered by type (person/company/lead)."""
    return contacts_for_tenant(tenant_id).filter(type=contact_type)


def contacts_search(tenant_id: int, query: str) -> QuerySet[Contact]:
    """Quick full-text search across name, phones, and emails."""
    qs = contacts_for_tenant(tenant_id)
    if not query:
        return qs
    return qs.filter(
        Q(name__icontains=query)
        | Q(phones__number__icontains=query)
        | Q(emails__address__icontains=query)
    ).distinct()


def contacts_with_tags(tenant_id: int, tag_ids: list[int]) -> QuerySet[Contact]:
    """Contacts that have ALL specified tags."""
    qs = contacts_for_tenant(tenant_id)
    for tag_id in tag_ids:
        qs = qs.filter(tags__id=tag_id)
    return qs.distinct()


def contact_by_id(tenant_id: int, pk: int) -> Contact:
    """Fetch a single contact or raise DoesNotExist."""
    return Contact.objects.get(tenant_id=tenant_id, pk=pk, deleted_at__isnull=True)


def contact_by_public_id(tenant_id: int, public_id: str) -> Contact:
    """Fetch a single contact by UUID or raise DoesNotExist."""
    return Contact.objects.get(tenant_id=tenant_id, public_id=public_id, deleted_at__isnull=True)


def tags_for_tenant(tenant_id: int) -> QuerySet[ContactTag]:
    """All tags for a tenant."""
    return ContactTag.objects.filter(tenant_id=tenant_id).order_by("name")
