# ─────────────────────────────────────────────────────────────────────────────
# app/services/planfix_jobs.py
# ─────────────────────────────────────────────────────────────────────────────
from __future__ import annotations
from werkzeug.local import LocalProxy
import traceback
from datetime import datetime
from typing import Tuple

import requests
from flask import Flask

from app.extensions import db, scheduler
from app.models.planfix_models import (
    PlanfixToken,
    PlanfixFetchLog,
    PlanfixCompany,
)

# ───────────── настройки ─────────────
PAGE_SIZE        = 100
CF_PAY_STATUS_1  = 132521
CF_PAY_STATUS_2  = 130477
# ────────────────────────────────────────────────────────────


def _headers(token_or_model: PlanfixToken | str) -> dict[str, str]:
    """
    Принимает либо модель PlanfixToken, либо сам raw-токен,
    возвращает заголовок Authorization.
    """
    if isinstance(token_or_model, PlanfixToken):
        tok = token_or_model.api_token
    else:
        tok = token_or_model
    return {"Authorization": f"Bearer {tok.strip()}"}


def _save(status: str, msg: str) -> None:
    """
    Пишем одну строку в журнал PlanfixFetchLog.
    """
    db.session.add(PlanfixFetchLog(status=status, message=msg))
    db.session.commit()


def _extract_status(cf_data: list[dict]) -> str:
    """
    Ищем сначала поле с id=CF_PAY_STATUS_1, если нет — CF_PAY_STATUS_2.
    Если ни того, ни другого нет — возвращаем 'Не сплачений'.
    """
    for field_id in (CF_PAY_STATUS_1, CF_PAY_STATUS_2):
        for cf in cf_data:
            if cf["field"]["id"] == field_id:
                # Здесь cf.get("value") тоже может вернуть пустую строку, проверим её:
                val = cf.get("value", "")
                if val:
                    return val
                else:
                    return "Не сплачений"
    # Если ни один CF_PAY_STATUS не найден, возвращаем явно «Не сплачений»
    return "Не сплачений"


def _upsert_companies(tasks: list[dict]) -> int:
    """
    INSERT / UPDATE записей в таблицу planfix_companies
    на основе списка задач (tasks) из PlanFix.
    Возвращает число созданных/обновлённых строк.
    """
    changed = 0
    for t in tasks:
        existing = (
            PlanfixCompany.query
            .filter_by(pf_company_id=t["id"])
            .first()
        )
        if existing is None:
            existing = PlanfixCompany(pf_company_id=t["id"])
            db.session.add(existing)

        existing.name       = t["name"]
        existing.pay_status = _extract_status(t.get("customFieldData", []))
        existing.updated_at = datetime.utcnow()
        changed += 1

    db.session.commit()
    return changed


def _do_test(api_login: str, api_token: str) -> Tuple[bool, str]:
    """
    Быстрый тест соединения: берём 1 задачу (task) с фильтром value=37.
    Используется в маршруте settings(), чтобы проверить, что api_login/api_token рабочие.
    """
    payload = {
        "offset": 0,
        "pageSize": 1,
        "filters": [{"type": 51, "operator": "equal", "value": 37}],
        "fields": "id,name",
    }
    try:
        r = requests.post(
            "https://smartcafe.planfix.ua/rest/task/list",
            json=payload,
            headers=_headers(api_token),
            timeout=10
        )
        r.raise_for_status()
        ok = r.json().get("result") == "success"
        return ok, "OK" if ok else str(r.json())
    except Exception as exc:
        return False, str(exc)


def fetch_contracts(*, app: Flask) -> None:
    """
    Основная задача для APScheduler. APScheduler при добавлении передаёт сюда Flask-app
    через kwargs={"app": app}.
    Внутри открываем app.app_context(), чтобы был доступ к db.session.
    """
    with app.app_context():
        token = PlanfixToken.query.filter_by(is_enabled=True).first()
        if token is None:
            _save("error", "No active token")
            return

        total_imported, offset = 0, 0
        try:
            while True:
                payload = {
                    "offset": offset,
                    "pageSize": PAGE_SIZE,
                    "filters": [{"type": 51, "operator": "equal", "value": 37}],
                    "fields": (
                        f"id,name,{CF_PAY_STATUS_1},{CF_PAY_STATUS_2},"
                        "customFieldData"
                    ),
                }
                resp = requests.post(
                    "https://smartcafe.planfix.ua/rest/task/list",
                    json=payload,
                    headers=_headers(token),
                    timeout=15
                )
                resp.raise_for_status()
                tasks = resp.json().get("tasks", [])
                if not tasks:
                    break

                total_imported += _upsert_companies(tasks)

                # Если получили неполный кусок — значит больше нет страниц
                if len(tasks) < PAGE_SIZE:
                    break
                offset += PAGE_SIZE

            _save("success", f"OK, imported {total_imported} companies")
        except Exception:
            _save("error", traceback.format_exc()[:1000])


def reschedule_job(token: PlanfixToken, *, app):
    """
    Пересоздаём / удаляем задачу planfix_fetch.
    Вызываем КАЖДЫЙ раз после изменения cron-строки или флага is_enabled.
    """
    # ─── 1) превращаем LocalProxy (current_app) → реальный Flask() объект ───
    if isinstance(app, LocalProxy):
        app = app._get_current_object()

    # ─── 2) Если такая задача уже есть, удаляем её ──────────────────────────
    if scheduler.get_job("planfix_fetch"):
        scheduler.remove_job("planfix_fetch")

    # ─── 3) Если интеграция включена, создаём задачу с нужным cron ─────────
    if token.is_enabled:
        m, h, *_ = token.cron_expr.split()
        scheduler.add_job(
            id="planfix_fetch",
            func=fetch_contracts,
            trigger="cron",
            minute=m,
            hour=h,
            kwargs={"app": app},
            replace_existing=True,
            misfire_grace_time=300,
        )
