import io
import json
import os
import sqlite3
import tempfile
from datetime import date, datetime
from decimal import Decimal

from flask import Blueprint, render_template, request, send_file, flash, redirect, url_for, abort
from flask_login import login_required, current_user

from app.extensions import db
from app.models.customer import Customer
from app.models.tag import Tag
from app.models.user import User
from app.services.permissions import require
from app.services.activity_logger import log_action, query_logs, CUSTOMER_ENTITY_TYPES
from app.services.jalali import iran_now, to_jalali_str
from config import APP_VERSION, SUPPORT_PHONE

bp = Blueprint("settings", __name__, url_prefix="/settings")

LOGS_PER_PAGE = 100

ACTION_FA = {
    "login": "ورود", "logout": "خروج", "search": "جستجو", "create": "ایجاد",
    "update": "ویرایش", "delete": "حذف", "print": "پرینت", "view": "مشاهده",
    "export": "خروجی گرفتن", "report": "گزارش‌گیری",
}
ENTITY_FA = {
    "customer": "مشتری", "call": "تماس", "user": "کاربر", "tag": "تگ",
    "tag_category": "دسته تگ", "customer_rating": "رتبه مشتری", "customer_tags": "تگ مشتری",
    "customer_report": "گزارش مشتریان", "salesperson_report": "گزارش فروشنده‌ها",
    "customer_next_call_date": "تاریخ تماس بعدی", "purchase_weight": "وزن خرید",
    "customer_import": "ورود از اکسل", "calls": "گزارش تماس‌ها", "salesperson": "گزارش فروشنده‌ها",
    "backup": "بک‌آپ", "customer_data_fix": "اصلاح داده‌ها",
    "task_folder": "پوشه کارها", "task_item": "تسک",
}
DETAIL_LABELS = {
    "query": "جستجو", "name": "نام", "count": "تعداد", "format": "فرمت", "tag_ids": "تگ‌ها",
    "salesperson_ids": "فروشنده‌ها", "next_call_date": "تاریخ تماس بعدی", "rating": "رتبه",
    "weight": "وزن (گرم)", "purchase_date": "تاریخ", "username": "نام کاربری", "created": "ثبت‌شده",
    "skipped": "رد‌شده", "total": "کل ردیف‌ها", "action": "عملیات", "call_date": "تاریخ تماس",
    "names": "نام‌های اصلاح‌شده", "phones": "شماره‌های اصلاح‌شده",
}
DETAIL_VALUES = {"restore": "بازیابی", "db": "فایل دیتابیس", "xlsx": "اکسل", "pdf": "PDF"}


@bp.route("/")
@login_required
@require("settings")
def index():
    deleted_customers = []
    if current_user.is_admin():
        deleted_customers = Customer.query.filter_by(is_deleted=True).order_by(Customer.first_name).all()
    return render_template("settings/index.html", app_version=APP_VERSION, support_phone=SUPPORT_PHONE,
                            deleted_customers=deleted_customers)


# ============================================================
# بک‌آپ
# ============================================================

def _excel_value(value):
    from openpyxl.cell.cell import ILLEGAL_CHARACTERS_RE

    if value is None:
        return ""
    if isinstance(value, Decimal):
        return float(value)
    if isinstance(value, (bool, int, float, date, datetime)):
        return value
    text = json.dumps(value, ensure_ascii=False) if isinstance(value, (dict, list)) else str(value)
    return ILLEGAL_CHARACTERS_RE.sub("", text)


@bp.route("/backup")
@login_required
@require("settings")
def backup():
    """خروجی اکسل کامل: همه‌ی جدول‌ها و همه‌ی ستون‌ها (به‌جز هش رمز عبور)."""
    from openpyxl import Workbook

    wb = Workbook()
    wb.remove(wb.active)
    for table in db.metadata.sorted_tables:
        columns = [c for c in table.columns if c.name != "password_hash"]
        ws = wb.create_sheet(title=table.name[:31])
        ws.append([c.name for c in columns])
        rows = db.session.execute(db.select(*columns).order_by(*table.primary_key.columns)).all()
        for row in rows:
            ws.append([_excel_value(v) for v in row])

    buffer = io.BytesIO()
    wb.save(buffer)
    buffer.seek(0)
    log_action(current_user, "export", "backup", details={"format": "xlsx"})
    stamp = iran_now().strftime("%Y-%m-%d_%H-%M-%S")
    return send_file(buffer, mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                      as_attachment=True, download_name=f"backup-{stamp}.xlsx")


@bp.route("/backup/database")
@login_required
@require("settings")
def backup_database():
    """بک‌آپ قابل‌بازگردانی: یک کپی سالم از کل فایل دیتابیس SQLite (فقط مدیر؛ شامل هش رمزهاست)."""
    if not current_user.is_admin():
        abort(403)
    url = db.engine.url
    if url.get_backend_name() != "sqlite" or not url.database:
        flash("بک‌آپ فایل دیتابیس فقط برای دیتابیس SQLite ممکن است", "error")
        return redirect(url_for("settings.index"))

    fd, tmp_path = tempfile.mkstemp(suffix=".db")
    os.close(fd)
    try:
        source = sqlite3.connect(url.database)
        target = sqlite3.connect(tmp_path)
        try:
            source.backup(target)  # کپی سازگار، حتی وقتی برنامه همزمان در حال نوشتنه
        finally:
            target.close()
            source.close()
        with open(tmp_path, "rb") as f:
            data = f.read()
    finally:
        os.remove(tmp_path)

    log_action(current_user, "export", "backup", details={"format": "db"})
    stamp = iran_now().strftime("%Y-%m-%d_%H-%M")
    return send_file(io.BytesIO(data), mimetype="application/vnd.sqlite3",
                      as_attachment=True, download_name=f"yasgold-backup-{stamp}.db")


# ============================================================
# لاگ فعالیت‌ها
# ============================================================

def _format_detail_value(key, value, users, tags):
    if key == "tag_ids" and isinstance(value, list):
        return "، ".join(tags.get(v, f"#{v}") for v in value) or "-"
    if key == "salesperson_ids" and isinstance(value, list):
        return "، ".join(users.get(v, f"#{v}") for v in value) or "-"
    if key == "call_date" and isinstance(value, str):
        try:
            return to_jalali_str(datetime.fromisoformat(value), with_time=True)
        except ValueError:
            return value
    if isinstance(value, list):
        return "، ".join(str(v) for v in value) or "-"
    if value is None:
        return "-"
    if isinstance(value, str):
        return DETAIL_VALUES.get(value, value)
    return str(value)


def _format_details(details, users, tags):
    if not details:
        return "-"
    if not isinstance(details, dict):
        return str(details)
    parts = [
        f"{DETAIL_LABELS.get(key, key)}: {_format_detail_value(key, value, users, tags)}"
        for key, value in details.items()
        if key != "customer_id"  # مشتری در ستون «مورد» با اسم و لینک نمایش داده می‌شه
    ]
    return " · ".join(parts) or "-"


def _log_display_rows(logs):
    """برای هر لاگ: عملیات و بخش به فارسی، «مورد» (مثلاً اسم مشتری با لینک پرونده) و جزئیات خوانا."""
    customer_ids, user_ids, tag_ids = set(), set(), set()
    for log in logs:
        details = log.details if isinstance(log.details, dict) else {}
        if log.entity_type in CUSTOMER_ENTITY_TYPES and log.entity_id:
            customer_ids.add(log.entity_id)
        if isinstance(details.get("customer_id"), int):
            customer_ids.add(details["customer_id"])
        if log.entity_type == "user" and log.entity_id:
            user_ids.add(log.entity_id)
        for key, bucket in (("tag_ids", tag_ids), ("salesperson_ids", user_ids)):
            if isinstance(details.get(key), list):
                bucket.update(v for v in details[key] if isinstance(v, int))

    customers = {c.id: c for c in Customer.query.filter(Customer.id.in_(customer_ids))} if customer_ids else {}
    users = {u.id: u.full_name for u in User.query.filter(User.id.in_(user_ids))} if user_ids else {}
    tags = {t.id: t.name for t in Tag.query.filter(Tag.id.in_(tag_ids))} if tag_ids else {}

    rows = []
    for log in logs:
        details = log.details if isinstance(log.details, dict) else {}
        target, target_url = None, None
        customer_id = log.entity_id if log.entity_type in CUSTOMER_ENTITY_TYPES else details.get("customer_id")
        if customer_id in customers:
            customer = customers[customer_id]
            target = f"{customer.first_name} {customer.last_name}"
            if not customer.is_deleted:
                target_url = url_for("customer_profile.show", customer_id=customer_id)
        elif log.entity_type == "user" and log.entity_id in users:
            target = users[log.entity_id]
        elif log.entity_type in ("tag", "tag_category") and details.get("name"):
            target = details["name"]
        rows.append({
            "log": log,
            "action": ACTION_FA.get(log.action_type, log.action_type),
            "entity": ENTITY_FA.get(log.entity_type, log.entity_type or "-"),
            "target": target,
            "target_url": target_url,
            "details": _format_details(log.details, users, tags),
        })
    return rows


@bp.route("/logs")
@login_required
@require("settings")
def logs():
    if not current_user.is_admin():
        abort(403)
    search = request.args.get("q", "").strip()
    action_filter = request.args.get("action", "")
    page = max(request.args.get("page", 1, type=int) or 1, 1)

    query = query_logs(action_type=action_filter or None, search=search or None)
    total = query.count()
    pages = max((total + LOGS_PER_PAGE - 1) // LOGS_PER_PAGE, 1)
    page = min(page, pages)
    page_logs = query.offset((page - 1) * LOGS_PER_PAGE).limit(LOGS_PER_PAGE).all()

    def page_url(number):
        args = request.args.to_dict()
        args["page"] = number
        return url_for("settings.logs", **args)

    return render_template(
        "settings/logs.html", rows=_log_display_rows(page_logs), action_fa=ACTION_FA,
        q=request.args.get("q", ""), selected_action=action_filter,
        page=page, pages=pages, total=total,
        prev_url=page_url(page - 1) if page > 1 else None,
        next_url=page_url(page + 1) if page < pages else None,
    )


@bp.route("/customers/<int:customer_id>/restore", methods=["POST"])
@login_required
@require("settings")
def restore_customer(customer_id):
    if not current_user.is_admin():
        abort(403)
    customer = db.session.get(Customer, customer_id) or abort(404)
    customer.is_deleted = False
    db.session.commit()
    log_action(current_user, "update", "customer", customer_id,
               {"name": f"{customer.first_name} {customer.last_name}", "action": "restore"})
    flash(f"«{customer.first_name} {customer.last_name}» بازیابی شد", "success")
    return redirect(url_for("settings.index"))
