"""Property rental report schedule stored on AppSetting.meta_data."""
from __future__ import annotations

import json
from datetime import date, timedelta
from typing import Any, Dict

from sqlalchemy.orm import Session

from app.models.app_setting import AppSetting

SCHEDULE_META_KEY = "billing_property_rental_report_schedule"

DEFAULT_SCHEDULE: Dict[str, Any] = {
    "enabled": False,
    "frequency": "monthly",
    "day_of_month": 1,
    "time": "08:00",
    "recipients": [],
    "last_run": None,
    "next_run": None,
}


def _load_meta(db: Session) -> Dict[str, Any]:
    inst = AppSetting.get_instance(db)
    try:
        return json.loads(inst.meta_data) if inst.meta_data else {}
    except json.JSONDecodeError:
        return {}


def _compute_next_run(day_of_month: int, from_date: date | None = None) -> str:
    ref = from_date or date.today()
    day = max(1, min(28, int(day_of_month)))
    if ref.day < day:
        nxt = ref.replace(day=day)
    else:
        if ref.month == 12:
            nxt = date(ref.year + 1, 1, day)
        else:
            nxt = date(ref.year, ref.month + 1, day)
    return nxt.isoformat()


def get_property_rental_schedule(db: Session) -> Dict[str, Any]:
    meta = _load_meta(db)
    stored = meta.get(SCHEDULE_META_KEY) or {}
    out = dict(DEFAULT_SCHEDULE)
    if isinstance(stored, dict):
        for k in out:
            if k in stored and stored[k] is not None:
                out[k] = stored[k]
    if out.get("enabled") and not out.get("next_run"):
        out["next_run"] = _compute_next_run(int(out.get("day_of_month") or 1))
    return out


def save_property_rental_schedule(db: Session, updates: Dict[str, Any]) -> Dict[str, Any]:
    inst = AppSetting.get_instance(db)
    meta = _load_meta(db)
    current = meta.get(SCHEDULE_META_KEY) or {}
    if not isinstance(current, dict):
        current = {}
    for key in DEFAULT_SCHEDULE:
        if key in updates and updates[key] is not None:
            current[key] = updates[key]
    if current.get("enabled"):
        dom = int(current.get("day_of_month") or 1)
        current["next_run"] = _compute_next_run(dom)
    else:
        current["next_run"] = None
    meta[SCHEDULE_META_KEY] = current
    inst.meta_data = json.dumps(meta)
    db.commit()
    return get_property_rental_schedule(db)


def mark_schedule_run(db: Session) -> Dict[str, Any]:
    """Record last run and advance next_run (called when report is generated on schedule)."""
    sched = get_property_rental_schedule(db)
    today = date.today().isoformat()
    sched["last_run"] = today
    freq = (sched.get("frequency") or "monthly").lower()
    dom = int(sched.get("day_of_month") or 1)
    base = date.fromisoformat(today)
    if freq == "quarterly":
        sched["next_run"] = (base + timedelta(days=90)).replace(day=min(dom, 28)).isoformat()
    else:
        sched["next_run"] = _compute_next_run(dom, base)
    return save_property_rental_schedule(db, sched)
