from __future__ import annotations

import re

from core.models import ChatTaskSource, TaskProject

SAFE_TASK_FLAGS_DEFAULTS = {
    "derive_enabled": True,
    "match_mode": "strict",
    "require_prefix": True,
    "allowed_prefixes": ["task:", "todo:"],
    "completion_enabled": True,
    "ai_title_enabled": True,
    "announce_task_id": False,
    "min_chars": 3,
}

WHATSAPP_GROUP_ID_RE = re.compile(r"^\d+@g\.us$")
WHATSAPP_DIRECT_ID_RE = re.compile(r"^\d+@s\.whatsapp\.net$")
WHATSAPP_BARE_ID_RE = re.compile(r"^\d+$")
SIGNAL_GROUP_ID_RE = re.compile(r"^group\.[A-Za-z0-9+/=]+$")
SIGNAL_UUID_RE = re.compile(
    r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
    re.IGNORECASE,
)
SIGNAL_PHONE_RE = re.compile(r"^\+\d+$")
SIGNAL_INTERNAL_ID_RE = re.compile(r"^[A-Za-z0-9+/=]+$")


def channel_variants(service: str, channel: str) -> list[str]:
    value = str(channel or "").strip()
    if not value:
        return []
    variants = [value]
    service_key = str(service or "").strip().lower()
    if service_key == "whatsapp":
        bare = value.split("@", 1)[0].strip()
        if bare and bare not in variants:
            variants.append(bare)
        direct = f"{bare}@s.whatsapp.net" if bare else ""
        if direct and direct not in variants:
            variants.append(direct)
        group = f"{bare}@g.us" if bare else ""
        if group and group not in variants:
            variants.append(group)
    if service_key == "signal":
        digits = re.sub(r"[^0-9]", "", value)
        if digits and digits not in variants:
            variants.append(digits)
        if digits:
            plus = f"+{digits}"
            if plus not in variants:
                variants.append(plus)
    return variants


def _normalize_whatsapp_identifier(identifier: str) -> str:
    value = str(identifier or "").strip()
    if not value:
        return ""
    if "/" in value or "?" in value or "#" in value:
        return ""
    if WHATSAPP_GROUP_ID_RE.fullmatch(value):
        return value
    if WHATSAPP_DIRECT_ID_RE.fullmatch(value):
        return value
    bare = value.split("@", 1)[0].strip()
    if not WHATSAPP_BARE_ID_RE.fullmatch(bare):
        return ""
    if value.endswith("@s.whatsapp.net"):
        return f"{bare}@s.whatsapp.net"
    return f"{bare}@g.us"


def _normalize_signal_identifier(identifier: str) -> str:
    value = str(identifier or "").strip()
    if not value:
        return ""
    if SIGNAL_GROUP_ID_RE.fullmatch(value):
        return value
    if SIGNAL_UUID_RE.fullmatch(value):
        return value.lower()
    if SIGNAL_PHONE_RE.fullmatch(value):
        return value
    if SIGNAL_INTERNAL_ID_RE.fullmatch(value):
        return value
    return ""


def normalize_channel_identifier(service: str, identifier: str) -> str:
    service_key = str(service or "").strip().lower()
    value = str(identifier or "").strip()
    if not value:
        return ""
    if service_key == "whatsapp":
        return _normalize_whatsapp_identifier(value)
    if service_key == "signal":
        return _normalize_signal_identifier(value)
    return value


def resolve_message_scope(message) -> tuple[str, str]:
    source_service = str(getattr(message, "source_service", "") or "").strip().lower()
    source_channel = str(getattr(message, "source_chat_id", "") or "").strip()
    if source_service != "web":
        return source_service, source_channel
    identifier = getattr(getattr(message, "session", None), "identifier", None)
    fallback_service = str(getattr(identifier, "service", "") or "").strip().lower()
    fallback_identifier = str(getattr(identifier, "identifier", "") or "").strip()
    if fallback_service and fallback_identifier and fallback_service != "web":
        return fallback_service, fallback_identifier
    return source_service, source_channel


def _project_name_candidate(service: str, channel_identifier: str, message=None) -> str:
    person_name = ""
    if message is not None:
        identifier = getattr(getattr(message, "session", None), "identifier", None)
        person = getattr(identifier, "person", None)
        person_name = str(getattr(person, "name", "") or "").strip()
    if person_name:
        return person_name[:255]
    raw = str(channel_identifier or "").strip()
    if str(service or "").strip().lower() == "whatsapp":
        raw = raw.split("@", 1)[0].strip()
    cleaned = re.sub(r"\s+", " ", raw).strip()
    if not cleaned:
        cleaned = "Chat"
    return f"Chat: {cleaned}"[:255]


def _ensure_unique_project_name(user, base_name: str) -> str:
    base = str(base_name or "").strip() or "Chat"
    if not TaskProject.objects.filter(user=user, name=base).exists():
        return base
    idx = 2
    while idx < 10000:
        candidate = f"{base} ({idx})"[:255]
        if not TaskProject.objects.filter(user=user, name=candidate).exists():
            return candidate
        idx += 1
    return f"{base} ({str(user.id)[:8]})"[:255]


def ensure_default_source_for_chat(
    *,
    user,
    service: str,
    channel_identifier: str,
    message=None,
):
    service_key = str(service or "").strip().lower()
    normalized_identifier = normalize_channel_identifier(
        service_key, channel_identifier
    )
    variants = channel_variants(service_key, normalized_identifier)
    if not service_key or not variants:
        return None
    existing = (
        ChatTaskSource.objects.filter(
            user=user,
            service=service_key,
            channel_identifier__in=variants,
        )
        .select_related("project", "epic")
        .order_by("-enabled", "-updated_at", "-created_at")
        .first()
    )
    if existing is not None:
        if not existing.enabled:
            existing.enabled = True
            existing.save(update_fields=["enabled", "updated_at"])
        return existing
    project_name = _ensure_unique_project_name(
        user,
        _project_name_candidate(service_key, normalized_identifier, message=message),
    )
    project = TaskProject.objects.create(
        user=user,
        name=project_name,
        settings=dict(SAFE_TASK_FLAGS_DEFAULTS),
    )
    return ChatTaskSource.objects.create(
        user=user,
        service=service_key,
        channel_identifier=normalized_identifier,
        project=project,
        epic=None,
        enabled=True,
        settings=dict(SAFE_TASK_FLAGS_DEFAULTS),
    )
