"""Forms / surveys / appointments repositories — Phase 11.6."""
from __future__ import annotations

from typing import Sequence
from uuid import UUID

from sqlalchemy import func, select

from app.models.forms import (
    AppointmentBookingRef,
    ExperienceAppointmentPage,
    ExperienceForm,
    ExperienceSurvey,
    FormSubmission,
    SurveyResponse,
)
from app.repositories.base import TenantBaseRepository
from app.specifications.forms import (
    AppointmentBookingListSpec,
    AppointmentPageListSpec,
    FormListSpec,
    FormSubmissionListSpec,
    SurveyListSpec,
    SurveyResponseListSpec,
)


def _list_filtered(session, model, tenant_id, offset, limit, spec):
    async def _run():
        base = select(model).where(
            model.tenant_id == tenant_id,
            model.is_deleted.is_(False),
        )
        count_base = select(func.count()).select_from(model).where(
            model.tenant_id == tenant_id,
            model.is_deleted.is_(False),
        )
        if spec is not None:
            base = spec.apply(base)
            for clause in spec.filter_clauses():
                count_base = count_base.where(clause)
        else:
            base = base.order_by(model.created_at.desc())
        total = int((await session.execute(count_base)).scalar_one())
        result = await session.execute(base.offset(offset).limit(limit))
        return result.scalars().all(), total

    return _run()


class ExperienceFormRepository(TenantBaseRepository[ExperienceForm]):
    model = ExperienceForm

    async def get_by_code(
        self, tenant_id: UUID, workspace_id: UUID, code: str
    ) -> ExperienceForm | None:
        stmt = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.workspace_id == workspace_id,
            self.model.code == code,
            self.model.is_deleted.is_(False),
        )
        return (await self.session.execute(stmt)).scalar_one_or_none()

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: FormListSpec | None = None,
    ) -> tuple[Sequence[ExperienceForm], int]:
        return await _list_filtered(
            self.session, self.model, tenant_id, offset, limit, spec
        )


class FormSubmissionRepository(TenantBaseRepository[FormSubmission]):
    model = FormSubmission

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: FormSubmissionListSpec | None = None,
    ) -> tuple[Sequence[FormSubmission], int]:
        return await _list_filtered(
            self.session, self.model, tenant_id, offset, limit, spec
        )


class ExperienceSurveyRepository(TenantBaseRepository[ExperienceSurvey]):
    model = ExperienceSurvey

    async def get_by_code(
        self, tenant_id: UUID, workspace_id: UUID, code: str
    ) -> ExperienceSurvey | None:
        stmt = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.workspace_id == workspace_id,
            self.model.code == code,
            self.model.is_deleted.is_(False),
        )
        return (await self.session.execute(stmt)).scalar_one_or_none()

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: SurveyListSpec | None = None,
    ) -> tuple[Sequence[ExperienceSurvey], int]:
        return await _list_filtered(
            self.session, self.model, tenant_id, offset, limit, spec
        )


class SurveyResponseRepository(TenantBaseRepository[SurveyResponse]):
    model = SurveyResponse

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: SurveyResponseListSpec | None = None,
    ) -> tuple[Sequence[SurveyResponse], int]:
        return await _list_filtered(
            self.session, self.model, tenant_id, offset, limit, spec
        )


class ExperienceAppointmentPageRepository(
    TenantBaseRepository[ExperienceAppointmentPage]
):
    model = ExperienceAppointmentPage

    async def get_by_code(
        self, tenant_id: UUID, workspace_id: UUID, code: str
    ) -> ExperienceAppointmentPage | None:
        stmt = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.workspace_id == workspace_id,
            self.model.code == code,
            self.model.is_deleted.is_(False),
        )
        return (await self.session.execute(stmt)).scalar_one_or_none()

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: AppointmentPageListSpec | None = None,
    ) -> tuple[Sequence[ExperienceAppointmentPage], int]:
        return await _list_filtered(
            self.session, self.model, tenant_id, offset, limit, spec
        )


class AppointmentBookingRefRepository(TenantBaseRepository[AppointmentBookingRef]):
    model = AppointmentBookingRef

    async def get_by_external_ref(
        self, tenant_id: UUID, appointment_page_id: UUID, external_booking_ref: str
    ) -> AppointmentBookingRef | None:
        stmt = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.appointment_page_id == appointment_page_id,
            self.model.external_booking_ref == external_booking_ref,
            self.model.is_deleted.is_(False),
        )
        return (await self.session.execute(stmt)).scalar_one_or_none()

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: AppointmentBookingListSpec | None = None,
    ) -> tuple[Sequence[AppointmentBookingRef], int]:
        return await _list_filtered(
            self.session, self.model, tenant_id, offset, limit, spec
        )
