# -*- coding: utf-8 -*-
"""
Адмін-панель керування користувачами.
Вхід лише для ролі 'admin' (див. @role_required).
"""
from flask import jsonify, send_file
from datetime import date, datetime, timedelta

from flask import (
    Blueprint,
    render_template,
    request,
    flash,
    redirect,
    url_for,
    current_app,
)
from flask_login import current_user
from app.database import get_db_connection
from app.security.rbac import role_required
from app.security.passwords import hash_password
from app.services.stats_service import StatsService

admin_bp = Blueprint("admin_bp", __name__)


# ─────────────────────────── Панель ────────────────────────────────
@admin_bp.route("/")
@role_required("admin")
def admin_panel():
    """
    Ділимо всі записи з таблиці users на дві групи:
      • PF-клієнти  (is_pf_client = 1) — без пагінації
      • Зовнішні    (is_pf_client = 0) — з пагінацією
    А також підтримуємо пошук (argument 'search') за username.
    """
    # 1. Параметри пошуку та пагінації для зовнішніх
    search_query = request.args.get("search", "").strip()
    page         = request.args.get("page", 1, type=int)
    per_page     = 5
    offset       = (page - 1) * per_page

    conn = get_db_connection()
    with conn.cursor() as cur:
        # ─────── 1.1. СПИСОК PF-клієнтів (без пагінації) ─────────────
        if search_query:
            like_pattern = f"%{search_query}%"
            cur.execute(
                "SELECT * FROM users WHERE is_pf_client=1 AND username LIKE %s ORDER BY created_at DESC",
                (like_pattern,),
            )
        else:
            cur.execute(
                "SELECT * FROM users WHERE is_pf_client=1 ORDER BY created_at DESC"
            )
        pf_clients = cur.fetchall()

        # ─────── 1.2. РАХУЄМО КІЛЬКІСТЬ ЗОВНІШНІХ КЛІЄНТІВ ─────────────
        if search_query:
            like_pattern = f"%{search_query}%"
            cur.execute(
                "SELECT COUNT(*) AS cnt FROM users WHERE is_pf_client=0 AND username LIKE %s",
                (like_pattern,),
            )
        else:
            cur.execute(
                "SELECT COUNT(*) AS cnt FROM users WHERE is_pf_client=0"
            )
        total_ext = cur.fetchone()["cnt"]

        # ─────── 1.3. ВИТЯГУЄМО СТОРІНКУ «ЗОВНІШНІ КЛІЄНТИ» ───────────
        if search_query:
            cur.execute(
                """
                SELECT *
                  FROM users
                 WHERE is_pf_client=0
                   AND username LIKE %s
                 ORDER BY created_at DESC
                 LIMIT %s OFFSET %s
                """,
                (like_pattern, per_page, offset),
            )
        else:
            cur.execute(
                """
                SELECT *
                  FROM users
                 WHERE is_pf_client=0
                 ORDER BY created_at DESC
                 LIMIT %s OFFSET %s
                """,
                (per_page, offset),
            )
        ext_clients = cur.fetchall()

    conn.close()

    # 2. Розрахунок пагінації для зовнішніх
    pages = (total_ext // per_page) + (1 if total_ext % per_page else 0)
    pagination = {
        "page":       page,
        "per_page":   per_page,
        "total":      total_ext,
        "pages":      pages,
        "has_prev":   page > 1,
        "has_next":   page < pages,
        "prev_num":   page - 1,
        "next_num":   page + 1,
        "iter_pages": lambda l=1, r=pages: _iter_pages(page, l, r, pages),
    }

    # 3. Витягуємо всі договори PlanFix для підстановки у шаблоні
    conn = get_db_connection()
    with conn.cursor() as cur:
        cur.execute(
            "SELECT pf_company_id AS id, name, pay_status "
            "FROM planfix_companies "
            "ORDER BY name"
        )
        pf_rows = cur.fetchall()
    conn.close()

    # 4. Формуємо мапу: pf_map[pf_company_id] = { id, name, pay_status }
    pf_map = {row["id"]: row for row in pf_rows}

    return render_template(
        "admin.html",
        pf_clients=pf_clients,
        ext_clients=ext_clients,
        search=search_query,
        pagination=pagination,
        today=date.today(),
        pf_map=pf_map,
    )


def _iter_pages(current_page, left, right, total_pages):
    """Генерує послідовність сторінок для пагінації."""
    last = 0
    for num in range(1, total_pages + 1):
        if (
            num <= left
            or (current_page - 2 < num < current_page + 2)
            or num > total_pages - right
        ):
            if last + 1 != num:
                yield None
            yield num
            last = num

# ────────────────────────── Створення ──────────────────────────────
@admin_bp.route("/create_user", methods=["GET", "POST"])
@role_required("admin")
def create_user():
    """
    • Зовнішній клієнт  → place = вільний текст, is_pf_client=0.
    • Клієнт PlanFix    → place пусте,  is_pf_client=1, pf_company_id = id договору.
    """
    if request.method == "POST":
        is_pf = request.form.get("is_our_client") == "on"

        # ── обовʼязкові поля
        username  = request.form.get("username", "").strip()
        password  = request.form.get("password", "")
        role      = request.form.get("role", "client")
        if not username or not password:
            flash("Заповніть логін і пароль", "warning"); return redirect(request.url)

        # ── place / pf_company_id
        if is_pf:
            pf_id = request.form.get("pf_contract_id")
            if not pf_id:
                flash("Оберіть договір PlanFix", "warning"); return redirect(request.url)
            is_pf_client, pf_company_id, place = 1, int(pf_id), ""
        else:
            place = request.form.get("place", "").strip()
            if not place:
                flash("Поле «Заклад (place)» обовʼязкове", "warning"); return redirect(request.url)
            is_pf_client, pf_company_id = 0, None

        # ── тариф / банки / ліміти
        plan            = request.form.get("subscription_plan", "start")
        available_banks = ",".join(request.form.getlist("available_banks"))
        limit           = current_app.config["PLAN_LIMITS"].get(plan)
        parse_count     = 999_999 if limit is None else limit

        # ── дати та хеш
        now          = datetime.utcnow()
        payment_date = now + timedelta(days=30)
        parse_reset  = now + timedelta(days=30)
        pw_hash      = hash_password(password)

        # ── INSERT
        conn = get_db_connection()
        with conn.cursor() as cur:
            cur.execute(
                """
                INSERT INTO users (
                    username, password_hash, role, place,
                    payment_date, created_at,
                    subscription_plan, parse_count, parse_reset_date,
                    available_banks,
                    is_pf_client, pf_company_id
                ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                """,
                (
                    username, pw_hash, role, place,
                    payment_date, now,
                    plan, parse_count, parse_reset,
                    available_banks,
                    is_pf_client, pf_company_id,
                ),
            )
            StatsService.log(current_user.id, "create_user", username)
        conn.commit(); conn.close()

        flash("Користувача створено", "success")
        return redirect(url_for("admin_bp.admin_panel"))

    # --- GET: список договорів для radio-кнопки
    with get_db_connection() as conn, conn.cursor() as cur:
        cur.execute("SELECT pf_company_id AS id, name, pay_status FROM planfix_companies ORDER BY name")
        contracts = cur.fetchall()

    return render_template(
        "create_user.html",
        all_banks=current_app.config["ALL_BANKS"],
        contracts=contracts,
    )

# ────────────────────────── Редагування ────────────────────────────
@admin_bp.route("/edit_user/<int:user_id>", methods=["GET", "POST"])
@role_required("admin")
def edit_user(user_id):
    # 1) Завантажуємо дані користувача
    with get_db_connection() as conn, conn.cursor() as cur:
        cur.execute("SELECT * FROM users WHERE id=%s", (user_id,))
        user = cur.fetchone()

    if not user:
        flash("Користувача не знайдено", "warning")
        return redirect(url_for("admin_bp.admin_panel"))

    # 2) Якщо POST — обробляємо збереження
    if request.method == "POST":
        is_pf = request.form.get("is_our_client") == "on"

        # Базові поля
        username   = request.form.get("username", user["username"]).strip()
        new_pass   = request.form.get("new_password", "")
        role       = request.form.get("role", user["role"])

        if not username:
            flash("Поле «Логін» не може бути порожнім", "warning")
            return redirect(request.url)

        # 3) Визначаємо поля place / pf_company_id / is_pf_client
        if is_pf:
            pf_id = request.form.get("pf_contract_id")
            if not pf_id:
                flash("Оберіть договір PlanFix", "warning")
                return redirect(request.url)
            is_pf_client   = 1
            pf_company_id  = int(pf_id)
            place          = ""     # для PF-клієнта поле place залишаємо порожнім
        else:
            place = request.form.get("place", "").strip()
            if not place:
                flash("Поле «Заклад (place)» обовʼязкове для зовнішнього клієнта", "warning")
                return redirect(request.url)
            is_pf_client  = 0
            pf_company_id = None

        # 4) Тариф / ліміти / банки
        subscription_plan = request.form.get("subscription_plan", user["subscription_plan"])
        parse_count       = int(request.form.get("parse_count", user["parse_count"]))
        available_banks   = ",".join(request.form.getlist("available_banks"))

        # 5) Дата оплати
        pay_date_str = request.form.get("payment_date", "")
        pay_date     = user["payment_date"]
        if pay_date_str:
            try:
                pay_date = datetime.strptime(pay_date_str, "%Y-%m-%dT%H:%M")
            except ValueError:
                flash("Невірний формат дати «Оплачено до»", "warning")

        # 6) Оновлюємо запис у БД
        with get_db_connection() as conn, conn.cursor() as cur:
            cur.execute(
                """
                UPDATE users
                   SET username=%s,
                       role=%s,
                       place=%s,
                       subscription_plan=%s,
                       payment_date=%s,
                       parse_count=%s,
                       available_banks=%s,
                       is_pf_client=%s,
                       pf_company_id=%s
                 WHERE id=%s
                """,
                (
                    username, role, place,
                    subscription_plan, pay_date,
                    parse_count, available_banks,
                    is_pf_client, pf_company_id,
                    user_id,
                ),
            )
            # Якщо задали новий пароль — оновлюємо його окремим запитом
            if new_pass:
                new_hash = hash_password(new_pass)
                cur.execute(
                    "UPDATE users SET password_hash=%s WHERE id=%s",
                    (new_hash, user_id),
                )
            conn.commit()

        StatsService.log(current_user.id, "edit_user", username)
        flash("Зміни збережено", "success")
        return redirect(url_for("admin_bp.admin_panel"))

    # 7) Якщо GET — витягуємо список всіх договорів PlanFix
    with get_db_connection() as conn, conn.cursor() as cur:
        cur.execute("""
            SELECT pf_company_id AS id, name, pay_status
              FROM planfix_companies
             ORDER BY name
        """)
        contracts = cur.fetchall()

    # 8) Визначаємо, чи цей користувач — клієнт PF
    is_pf_client = bool(user.get("is_pf_client"))

    # 9) Рендеримо шаблон, передаємо user та contracts
    return render_template(
        "edit_user.html",
        user=user,
        all_banks=current_app.config["ALL_BANKS"],
        contracts=contracts,
        is_our_client=is_pf_client,   # шаблон показуватиме блок «Наш клієнт» якщо True
    )




# ─────────────────────────── Видалення ─────────────────────────────
@admin_bp.route("/delete_user/<int:user_id>", methods=["GET"])
@role_required("admin")
def delete_user(user_id):
    conn = get_db_connection()
    with conn.cursor() as cur:
        cur.execute("DELETE FROM users WHERE id=%s", (user_id,))
    conn.commit()
    conn.close()

    flash("Користувача видалено", "info")
    return redirect(url_for("admin_bp.admin_panel"))

# ─────────────────────────── DASHBOARD ────────────────────────────
@admin_bp.route("/dashboard")
@role_required("admin")
def admin_dashboard():
    # ── 1) KPI та інша «статична» статистика (ген/нові клієнти) ──────────
    kpi = StatsService.kpi_counters()
    top_users = StatsService.top_users(5)
    daily_stats = StatsService.daily_generations(30)  # дефолт – 30 днів
    plans_breakdown = StatsService.plans_breakdown()

    # ── 2) Пагінація для «Останніх дій» ────────────────────────────────
    page     = request.args.get("page", 1, type=int)
    per_page = 10
    offset   = (page - 1) * per_page

    conn = get_db_connection()
    with conn.cursor() as cur:
        # Всю кількість рядків беремо для того, щоб порахувати pages
        cur.execute("SELECT COUNT(*) AS cnt FROM activity_log")
        total = cur.fetchone()["cnt"]

        # Вибираємо останні події, ліми максимум per_page
        cur.execute(
            """
            SELECT 
              a.created_at AS created_at, 
              u.username    AS username, 
              a.action      AS action, 
              a.obj_info    AS obj_info
            FROM activity_log AS a
            LEFT JOIN users AS u ON u.id = a.user_id
            ORDER BY a.created_at DESC
            LIMIT %s OFFSET %s
            """,
            (per_page, offset),
        )
        last_events = cur.fetchall()
    conn.close()

    pages = (total // per_page) + (1 if total % per_page else 0)
    pagination = {
        "page":     page,
        "per_page": per_page,
        "total":    total,
        "pages":    pages,
        "has_prev": page > 1,
        "has_next": page < pages,
        "prev_num": page - 1,
        "next_num": page + 1,
    }

    ctx = {
        "kpi": kpi,
        "top_users": top_users,
        "daily_stats": daily_stats,
        "plans_breakdown": plans_breakdown,
        "last_events": last_events,
        "pagination": pagination,
    }
    return render_template("admin_dashboard.html", **ctx)



# ───────────────────────────── AJAX: Дані для графіка при зміні періоду ─────────────────────
@admin_bp.route("/dashboard/data")
@role_required("admin")
def admin_dashboard_data():
    """
    Возвращает JSON статистики генераций за запрошенный период.

    Query-param:
        days (int) – кол-во дней (7 / 30 / 90). По умолчанию 30.

    Response-schema:
        {
          "labels": ["2025-06-01", "2025-06-02", ...],
          "values": [3, 7, ...]
        }
    """
    days = request.args.get("days", 30, type=int)
    # ограничиваем диапазон, чтобы не запрашивали «миллион» дней
    days = max(1, min(days, 365))

    stats = StatsService.daily_generations(days)
    payload = {
        "labels": [row["day"].isoformat() for row in stats],  # YYYY-MM-DD
        "values": [row["cnt"] for row in stats],
    }
    return jsonify(payload)



# ───────────────────────────── Excel-експорт графіка ─────────────────────────────
@admin_bp.route("/dashboard/export_xlsx")
@role_required("admin")
def admin_dashboard_xlsx():
    """
    Генерує Excel-файл (xlsx) з даними генерацій за останні 365 днів
    і видає його як завантаження.
    """
    mem = StatsService.xlsx_daily_generations(days=365)
    return send_file(
        mem,
        mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        as_attachment=True,
        download_name="generations_last365.xlsx",
    )