"""Contact application service — Phase 6.1."""
from __future__ import annotations

from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.events.publisher import InMemoryEventPublisher, get_event_publisher
from app.events.types import CrmEventType
from app.models.foundation import Contact
from app.models.types import AuditAction
from app.repositories.foundation import ContactRepository
from app.schemas.foundation import ContactCreate, ContactUpdate
from app.services.audit_service import AuditService
from app.validators import validate_email, validate_non_empty, validate_phone
from shared.exceptions import NotFoundError
from shared.security import CurrentUser


def _apply_update(entity, data: dict) -> None:
    for key, value in data.items():
        setattr(entity, key, value)


class ContactService:
    def __init__(
        self,
        session: AsyncSession,
        publisher: InMemoryEventPublisher | None = None,
    ) -> None:
        self.session = session
        self.repo = ContactRepository(session)
        self.audit = AuditService(session)
        self.publisher = publisher or get_event_publisher()

    async def create(
        self,
        tenant_id: UUID,
        body: ContactCreate,
        *,
        actor: CurrentUser | None = None,
    ) -> Contact:
        actor_id = actor.user_id if actor else None
        first = validate_non_empty(body.first_name, field="first_name")
        last = validate_non_empty(body.last_name, field="last_name")
        email = validate_email(body.email)
        phone = validate_phone(body.phone)
        emails = None
        if body.emails:
            emails = [validate_email(e, required=True) for e in body.emails]
        phones = None
        if body.phones:
            phones = [validate_phone(p, field="phone") for p in body.phones]
            phones = [p for p in phones if p]
        entity = Contact(
            tenant_id=tenant_id,
            first_name=first,
            last_name=last,
            full_name=f"{first} {last}".strip(),
            kind=body.kind,
            contact_type_id=body.contact_type_id,
            email=email,
            phone=phone,
            emails=emails,
            phones=phones,
            default_contact_method=body.default_contact_method,
            social_links=body.social_links,
            birthday=body.birthday,
            job_title=body.job_title,
            job_position=body.job_position or body.job_title,
            status=body.status,
            organization_id=body.organization_id,
            owner_user_id=body.owner_user_id,
            notes_summary=body.notes_summary,
            created_by=actor_id,
            updated_by=actor_id,
        )
        await self.repo.add(entity)
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="contact",
            entity_id=entity.id,
            action=AuditAction.CREATE,
            actor_user_id=actor_id,
        )
        await self.session.commit()
        await self.session.refresh(entity)
        self.publisher.publish(
            event_type=CrmEventType.CONTACT_CREATED,
            aggregate_type="contact",
            aggregate_id=entity.id,
            tenant_id=tenant_id,
            payload={"email": entity.email, "full_name": entity.full_name},
        )
        return entity

    async def update(
        self,
        tenant_id: UUID,
        contact_id: UUID,
        body: ContactUpdate,
        *,
        actor: CurrentUser | None = None,
    ) -> Contact:
        entity = await self.repo.get(tenant_id, contact_id)
        if entity is None:
            raise NotFoundError("مخاطب یافت نشد", error_code="contact_not_found")
        actor_id = actor.user_id if actor else None
        data = body.model_dump(exclude_unset=True)
        if "first_name" in data:
            data["first_name"] = validate_non_empty(data["first_name"], field="first_name")
        if "last_name" in data:
            data["last_name"] = validate_non_empty(data["last_name"], field="last_name")
        if "email" in data:
            data["email"] = validate_email(data["email"])
        if "phone" in data:
            data["phone"] = validate_phone(data["phone"])
        if "emails" in data and data["emails"] is not None:
            data["emails"] = [
                validate_email(e, required=True) for e in data["emails"]
            ]
        if "phones" in data and data["phones"] is not None:
            data["phones"] = [
                p
                for p in (validate_phone(p, field="phone") for p in data["phones"])
                if p
            ]
        first = data.get("first_name", entity.first_name)
        last = data.get("last_name", entity.last_name)
        data["full_name"] = f"{first} {last}".strip()
        _apply_update(entity, data)
        entity.updated_by = actor_id
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="contact",
            entity_id=entity.id,
            action=AuditAction.UPDATE,
            actor_user_id=actor_id,
            changes=data,
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity

    async def soft_delete(
        self,
        tenant_id: UUID,
        contact_id: UUID,
        *,
        actor: CurrentUser | None = None,
    ) -> Contact:
        entity = await self.repo.get(tenant_id, contact_id)
        if entity is None:
            raise NotFoundError("مخاطب یافت نشد", error_code="contact_not_found")
        actor_id = actor.user_id if actor else None
        await self.repo.soft_delete(entity, deleted_by=actor_id)
        await self.audit.record(
            tenant_id=tenant_id,
            entity_type="contact",
            entity_id=entity.id,
            action=AuditAction.DELETE,
            actor_user_id=actor_id,
        )
        await self.session.commit()
        await self.session.refresh(entity)
        return entity

    async def get(self, tenant_id: UUID, contact_id: UUID) -> Contact:
        entity = await self.repo.get(tenant_id, contact_id)
        if entity is None:
            raise NotFoundError("مخاطب یافت نشد", error_code="contact_not_found")
        return entity

    async def list(self, tenant_id: UUID, *, offset: int, limit: int):
        return await self.repo.list_by_tenant(tenant_id, offset=offset, limit=limit)
