from __future__ import annotations

import asyncio
import json
import logging
from datetime import datetime, timedelta, timezone

from sqlalchemy import text, select

from app.database import AsyncSessionLocal
from app.plugins.cin7_import.client import Cin7Client
from app.plugins.cin7_import.models import Cin7SyncState
from app.plugins.cin7_import.services import Cin7ImportService

logger = logging.getLogger("uvicorn.error")

_sync_lock = asyncio.Lock()
_last_cleanup: datetime | None = None


async def _cleanup_old_logs():
    global _last_cleanup
    now = datetime.now(timezone.utc)
    if _last_cleanup and (now - _last_cleanup) < timedelta(hours=24):
        return
    try:
        async with AsyncSessionLocal() as db:
            result = await db.execute(
                text("DELETE FROM cin7_sync_logs WHERE created_at < :cutoff"),
                {"cutoff": now - timedelta(days=7)},
            )
            await db.commit()
            deleted = result.rowcount
            if deleted:
                logger.info("[cin7_scheduler] Cleaned up %d old sync logs", deleted)
        _last_cleanup = now
    except Exception as e:
        logger.error("[cin7_scheduler] Log cleanup error: %s", e)


def _cfg_bool(cfg: dict, key: str, default: bool = True) -> bool:
    val = cfg.get(key)
    if val is None:
        return default
    if isinstance(val, bool):
        return val
    return str(val).lower() == "true"


async def _should_sync(db, tenant_id: int, resource: str, interval_minutes: int) -> bool:
    result = await db.execute(
        select(Cin7SyncState.last_success_at).where(
            Cin7SyncState.tenant_id == tenant_id,
            Cin7SyncState.resource == resource,
        ).limit(1)
    )
    last = result.scalar_one_or_none()
    if not last:
        return True
    cutoff = datetime.now(timezone.utc) - timedelta(minutes=interval_minutes)
    last_utc = last.replace(tzinfo=timezone.utc) if last.tzinfo is None else last
    return last_utc < cutoff


async def _scheduler_loop():
    logger.info("[cin7_scheduler] Loop started, waiting 30s before first run...")
    await asyncio.sleep(30)
    while True:
        try:
            await _cleanup_old_logs()
            if _sync_lock.locked():
                await asyncio.sleep(60)
                continue
            async with _sync_lock:
                await _run_all_tenants()
        except Exception as e:
            logger.error("[cin7_scheduler] Loop error: %s", e, exc_info=True)
        await asyncio.sleep(60)


async def _run_all_tenants():
    async with AsyncSessionLocal() as db:
        rows = await db.execute(
            text(
                "SELECT pc.tenant_id, pc.config "
                "FROM plugin_configs pc "
                "WHERE pc.plugin_name = 'cin7_import' AND pc.is_active = 1"
            )
        )
        configs = rows.fetchall()

    for row in configs:
        tenant_id = row[0]
        raw_cfg = row[1]
        cfg = raw_cfg if isinstance(raw_cfg, dict) else (json.loads(raw_cfg) if raw_cfg else {})

        username = cfg.get("api_username")
        password = cfg.get("api_password")
        if not username or not password:
            continue

        client = Cin7Client(username, password, cfg.get("base_url"))
        sync_options = {
            "sync_price": _cfg_bool(cfg, "sync_price", True),
            "auto_delist_zero_stock": _cfg_bool(cfg, "auto_delist_zero_stock", True),
            "sync_status": _cfg_bool(cfg, "sync_status", True),
            "publish_field": (cfg.get("publish_field") or "").strip(),
            "publish_field_value": (cfg.get("publish_field_value") or "").strip(),
            "price_col_default": (cfg.get("price_col_default") or "").strip(),
            "price_col_market": (cfg.get("price_col_market") or "").strip(),
            "price_col_cost": (cfg.get("price_col_cost") or "").strip(),
        }

        async with AsyncSessionLocal() as db:
            try:
                svc = Cin7ImportService(db, tenant_id, client, sync_options=sync_options)

                products_interval = int(cfg.get("products_interval_minutes") or 30)
                contacts_interval = int(cfg.get("contacts_interval_minutes") or 60)

                if _cfg_bool(cfg, "sync_categories", True):
                    if await _should_sync(db, tenant_id, "categories", products_interval):
                        await svc.sync_categories(mode="auto")

                if _cfg_bool(cfg, "sync_brands", True):
                    if await _should_sync(db, tenant_id, "brands", products_interval):
                        await svc.sync_brands(mode="auto")

                if _cfg_bool(cfg, "sync_products", True):
                    if await _should_sync(db, tenant_id, "products", products_interval):
                        await svc.sync_products(mode="auto")

                if _cfg_bool(cfg, "sync_contacts", True):
                    if await _should_sync(db, tenant_id, "contacts", contacts_interval):
                        await svc.sync_contacts(mode="auto")

            except Exception as e:
                logger.error("[cin7_scheduler] Auto sync error for tenant %d: %s", tenant_id, e, exc_info=True)


def register_scheduler(app):
    logger.info("[cin7_scheduler] ====== Registering scheduler task ======")
    asyncio.ensure_future(_scheduler_loop())
