"""Daily expiry reminder generation and listing."""

from __future__ import annotations

from datetime import date, datetime, timedelta
from zoneinfo import ZoneInfo

from sqlalchemy.orm import Session, joinedload

from app.core.config import get_settings
from app.models.enums import PolicyStatus
from app.models.policy import Policy
from app.models.reminder_log import ReminderLog
from app.services.whatsapp_service import WhatsAppService, get_agency_name
from app.utils.quiet_hours import is_quiet_hours

REMINDER_OFFSETS = (30, 15, 7, 3, 1)
SKIP_STATUSES = {PolicyStatus.RENEWED, PolicyStatus.CANCELLED}


def today_in_timezone(tz_name: str | None = None) -> date:
    tz = ZoneInfo(tz_name or get_settings().timezone)
    return datetime.now(tz).date()


def offset_for_expiry(expiry_date: date, run_date: date) -> int | None:
    if not expiry_date:
        return None
    days_left = (expiry_date - run_date).days
    return days_left if days_left in REMINDER_OFFSETS else None


class ReminderService:
    def __init__(self, db: Session):
        self.db = db

    def generate_daily_reminders(self, run_date: date | None = None) -> dict:
        run_date = run_date or today_in_timezone()
        created = 0
        skipped = 0

        policies = (
            self.db.query(Policy)
            .filter(
                Policy.deleted_at.is_(None),
                Policy.policy_end_date.isnot(None),
                Policy.status.notin_(list(SKIP_STATUSES)),
            )
            .all()
        )

        for policy in policies:
            offset = offset_for_expiry(policy.policy_end_date, run_date)
            if offset is None:
                continue

            exists = (
                self.db.query(ReminderLog.id)
                .filter(
                    ReminderLog.policy_id == policy.id,
                    ReminderLog.offset_days == offset,
                    ReminderLog.reminder_date == run_date,
                )
                .first()
            )
            if exists:
                skipped += 1
                continue

            self.db.add(
                ReminderLog(
                    policy_id=policy.id,
                    agency_id=policy.agency_id,
                    reminder_date=run_date,
                    offset_days=offset,
                    status="pending",
                )
            )
            created += 1

        if created:
            self.db.commit()

        return {"reminder_date": run_date.isoformat(), "created": created, "skipped_existing": skipped}

    def list_reminders(self, agency_id: int, reminder_date: date | None = None) -> list[dict]:
        reminder_date = reminder_date or today_in_timezone()
        rows = (
            self.db.query(ReminderLog)
            .options(
                joinedload(ReminderLog.policy).joinedload(Policy.customer),
                joinedload(ReminderLog.policy).joinedload(Policy.vehicle),
                joinedload(ReminderLog.policy).joinedload(Policy.insurance_company),
            )
            .filter(ReminderLog.agency_id == agency_id, ReminderLog.reminder_date == reminder_date)
            .order_by(ReminderLog.offset_days.asc(), ReminderLog.id.asc())
            .all()
        )

        items: list[dict] = []
        for row in rows:
            policy = row.policy
            if not policy or policy.deleted_at is not None:
                continue
            if policy.status in SKIP_STATUSES:
                continue
            items.append(
                {
                    "id": row.id,
                    "policy_id": policy.id,
                    "reminder_date": row.reminder_date.isoformat(),
                    "offset_days": row.offset_days,
                    "status": row.status,
                    "customer_name": policy.customer.name if policy.customer else None,
                    "customer_mobile": policy.customer.mobile if policy.customer else None,
                    "policy_number": policy.policy_number,
                    "vehicle_registration": policy.vehicle.registration_number if policy.vehicle else None,
                    "insurance_company_name": policy.insurance_company.name if policy.insurance_company else None,
                    "policy_end_date": policy.policy_end_date.isoformat() if policy.policy_end_date else None,
                    "pending_amount": float(policy.pending_amount),
                }
            )
        return items

    def mark_reminder_status(self, agency_id: int, reminder_id: int, status: str, notes: str | None = None) -> dict:
        row = (
            self.db.query(ReminderLog)
            .filter(ReminderLog.id == reminder_id, ReminderLog.agency_id == agency_id)
            .first()
        )
        if not row:
            from app.core.exceptions import NotFoundError

            raise NotFoundError("Reminder not found")
        row.status = status
        if notes is not None:
            row.notes = notes
        self.db.commit()
        self.db.refresh(row)
        return {
            "id": row.id,
            "status": row.status,
            "notes": row.notes,
        }

    def send_whatsapp_for_today(self, run_date: date | None = None) -> dict:
        run_date = run_date or today_in_timezone()
        settings = get_settings()

        if not settings.whatsapp_cloud_enabled:
            return {"sent": 0, "fallback": 0, "skipped": 0, "reason": "whatsapp_disabled"}

        if is_quiet_hours():
            return {"sent": 0, "fallback": 0, "skipped": 0, "reason": "quiet_hours"}

        rows = (
            self.db.query(ReminderLog)
            .options(joinedload(ReminderLog.policy).joinedload(Policy.customer))
            .filter(
                ReminderLog.reminder_date == run_date,
                ReminderLog.status == "pending",
            )
            .all()
        )

        whatsapp = WhatsAppService(self.db)
        sent = 0
        fallback = 0
        skipped = 0

        for row in rows:
            policy = row.policy
            if not policy or policy.deleted_at is not None or policy.status in SKIP_STATUSES:
                skipped += 1
                continue
            if not policy.customer or policy.customer.whatsapp_opt_out:
                row.status = "skipped_opt_out"
                skipped += 1
                continue

            agency_name = get_agency_name(self.db, row.agency_id)
            try:
                result = whatsapp.send_automatic_renewal(
                    policy,
                    agency_name,
                    reminder_offset_days=row.offset_days,
                )
                if result["mode"] == "automatic":
                    row.status = "whatsapp_sent"
                    sent += 1
                else:
                    row.status = "manual_fallback"
                    row.notes = result.get("failure_reason")
                    fallback += 1
            except Exception as exc:
                row.status = "failed"
                row.notes = str(exc)
                skipped += 1

        self.db.commit()
        return {"sent": sent, "fallback": fallback, "skipped": skipped, "reason": None}
