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

from app.extensions import db
from app.models.customer import Customer
from app.models.import_job import ImportJob
from app.services.permissions import require
from app.services.activity_logger import log_action
from app.services import customer_service, photo_storage, customer_import, listing
from app.services.jalali import jalali_str_to_gregorian_date, to_jalali_str
from app.blueprints.customer_account.forms import CustomerForm
from config import CUSTOMER_RATINGS

bp = Blueprint("customer_account", __name__, url_prefix="/customers")

STATUS_FA = dict(customer_service.STATUS_CHOICES)


def _render_form(form, customer, **extra):
    return render_template("customer_account/form.html", form=form, customer=customer, status_fa=STATUS_FA, **extra)


def _phone_variants(raw):
    """شماره همون‌طور که تایپ شده + شکل یکدست‌شده‌اش (داده‌های قدیمی ممکنه هنوز یکدست نشده باشن).
    شماره‌ی خالی یا «0» اصلاً بررسی نمی‌شه، وگرنه با همه‌ی مشتری‌های بدون شماره «تکراری» حساب می‌شد."""
    normalized = customer_service.normalize_phone(raw)
    if not normalized:
        return set()
    return {normalized, (raw or "").strip()}


def _check_duplicate_phone(form, exclude_id=None):
    mobiles = _phone_variants(form.mobile_phone.data)
    landlines = _phone_variants(form.landline_phone.data)
    if not mobiles and not landlines:
        return None

    query = Customer.query.filter(Customer.is_deleted.is_(False))
    if exclude_id:
        query = query.filter(Customer.id != exclude_id)

    existing = None
    matched_phone = None
    if mobiles:
        existing = query.filter(Customer.mobile_phone.in_(mobiles)).first()
        matched_phone = customer_service.normalize_phone(form.mobile_phone.data)
    if not existing and landlines:
        existing = query.filter(Customer.landline_phone.in_(landlines)).first()
        matched_phone = customer_service.normalize_phone(form.landline_phone.data)

    if existing:
        return {
            "existing_id": existing.id,
            "existing_name": f"{existing.first_name} {existing.last_name}",
            "phone": matched_phone,
        }
    return None


def _customer_code_taken(code, exclude_id=None) -> bool:
    if not code:
        return False
    query = Customer.query.filter(Customer.customer_code == code)
    if exclude_id:
        query = query.filter(Customer.id != exclude_id)
    return db.session.query(query.exists()).scalar()


def _populate_customer(customer, form):
    customer.first_name = form.first_name.data.strip()
    customer.last_name = form.last_name.data.strip()
    customer.city = form.city.data.strip() if form.city.data else None
    customer.gender = form.gender.data or None
    customer.landline_phone = customer_service.normalize_phone(form.landline_phone.data)
    customer.mobile_phone = customer_service.normalize_phone(form.mobile_phone.data)
    customer.customer_code = form.customer_code.data.strip() if form.customer_code.data else None
    customer.referrer_text = form.referrer_text.data.strip() if form.referrer_text.data else None
    customer.how_found = form.how_found.data.strip() if form.how_found.data else None
    customer.instagram_id = form.instagram_id.data.strip() if form.instagram_id.data else None
    # «غیرفعال» دستی می‌مونه؛ در غیر این صورت بعد از ذخیره، وضعیت از روی تماس و خرید حساب می‌شه
    if form.status.data == "inactive":
        customer.status = "inactive"
    elif customer.status in (None, "inactive"):
        customer.status = "new"
    customer.address = form.address.data.strip() if form.address.data else None
    customer.description = form.description.data.strip() if form.description.data else None
    if form.birth_date.data and form.birth_date.data.strip():
        try:
            customer.birth_date = jalali_str_to_gregorian_date(form.birth_date.data.strip())
        except Exception:
            customer.birth_date = None
    else:
        customer.birth_date = None


@bp.route("/new", methods=["GET", "POST"])
@login_required
@require("customer_account")
def new():
    form = CustomerForm()
    if request.method == "GET":
        form.customer_code.data = customer_service.next_customer_code()

    if form.validate_on_submit():
        if request.form.get("confirm_duplicate") != "1":
            dup = _check_duplicate_phone(form)
            if dup:
                return _render_form(form, None, duplicate_warning=dup)

        code = form.customer_code.data.strip() if form.customer_code.data else None
        if _customer_code_taken(code):
            flash("این کد مشتری قبلاً استفاده شده است", "error")
            return _render_form(form, None)

        customer = Customer(created_by=current_user.id)
        _populate_customer(customer, form)
        db.session.add(customer)
        db.session.flush()

        if form.photo.data:
            try:
                customer.photo_path = photo_storage.save_photo(form.photo.data, customer.id)
            except photo_storage.InvalidPhotoError as exc:
                db.session.rollback()
                flash(str(exc), "error")
                return _render_form(form, None)

        db.session.commit()
        customer_service.refresh_statuses([customer.id])
        log_action(current_user, "create", "customer", customer.id,
                   {"name": f"{customer.first_name} {customer.last_name}"})
        flash(f"«{customer.first_name} {customer.last_name}» با موفقیت ثبت شد — می‌توانید مشتری بعدی را وارد کنید", "success")
        return redirect(url_for("customer_account.new"))

    return _render_form(form, None)


@bp.route("/<int:customer_id>/edit", methods=["GET", "POST"])
@login_required
@require("customer_account")
def edit(customer_id):
    customer = db.session.get(Customer, customer_id) or abort(404)
    if customer.is_deleted:
        abort(404)
    form = CustomerForm(obj=customer)
    if request.method == "GET":
        if customer.birth_date:
            form.birth_date.data = to_jalali_str(customer.birth_date)
        form.gender.data = customer.gender or ""
        form.status.data = "inactive" if customer.status == "inactive" else "auto"

    if form.validate_on_submit():
        if request.form.get("confirm_duplicate") != "1":
            dup = _check_duplicate_phone(form, exclude_id=customer.id)
            if dup:
                return _render_form(form, customer, duplicate_warning=dup)

        # قبل از هر تغییری روی آبجکت چک بشه، وگرنه ذخیره با کد تکراری خطای سرور می‌داد
        code = form.customer_code.data.strip() if form.customer_code.data else None
        if _customer_code_taken(code, exclude_id=customer.id):
            flash("این کد مشتری قبلاً برای مشتری دیگری استفاده شده است", "error")
            return _render_form(form, customer)

        old_photo = customer.photo_path
        _populate_customer(customer, form)
        if form.photo.data:
            try:
                customer.photo_path = photo_storage.save_photo(form.photo.data, customer.id)
            except photo_storage.InvalidPhotoError as exc:
                db.session.rollback()
                flash(str(exc), "error")
                return _render_form(form, customer)
        db.session.commit()
        if old_photo and old_photo != customer.photo_path:
            photo_storage.delete_photo(old_photo)
        customer_service.refresh_statuses([customer.id])
        log_action(current_user, "update", "customer", customer.id,
                   {"name": f"{customer.first_name} {customer.last_name}"})
        flash("اطلاعات مشتری با موفقیت ذخیره شد", "success")
        return redirect(url_for("customer_profile.show", customer_id=customer.id))

    return _render_form(form, customer)


@bp.route("/remove", methods=["GET"])
@login_required
@require("customer_account")
def remove():
    search = request.args.get("q", "").strip()
    city = request.args.get("city") or None
    status = request.args.get("status") or None
    rating = request.args.get("rating") or None
    sort = request.args.get("sort", "joined_desc")
    filters = dict(search=search, city=city, status=status, rating=rating)

    total = customer_service.count_customers(**filters)
    results = customer_service.list_customers(**filters, sort=sort, limit=listing.page_limit())
    cities = customer_service.distinct_cities()

    return render_template(
        "customer_account/remove.html",
        results=results, total=total, cities=cities,
        statuses=customer_service.STATUS_CHOICES, ratings=CUSTOMER_RATINGS,
        sorts=customer_service.SORT_OPTIONS,
        q=search, selected_city=city, selected_status=status,
        selected_rating=rating, selected_sort=sort,
    )


@bp.route("/<int:customer_id>/delete", methods=["POST"])
@login_required
@require("customer_account")
def delete(customer_id):
    customer = db.session.get(Customer, customer_id) or abort(404)
    name = f"{customer.first_name} {customer.last_name}"
    customer.is_deleted = True  # حذف نرم — طبق تصمیم کاربر برای نسخه آنلاین چندکاربره
    db.session.commit()
    log_action(current_user, "delete", "customer", customer.id, {"name": name})
    flash(f"«{name}» حذف شد", "success")
    return redirect(url_for("customer_account.remove"))


@bp.route("/import")
@login_required
@require("customer_account")
def import_page():
    return render_template("customer_account/import.html")


@bp.route("/import/template")
@login_required
@require("customer_account")
def import_template():
    buffer = customer_import.build_template_excel()
    return send_file(
        buffer, mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        as_attachment=True, download_name="قالب-وارد-کردن-مشتریان.xlsx",
    )


@bp.route("/import/check", methods=["POST"])
@login_required
@require("customer_account")
def import_check():
    file = request.files.get("file")
    if not file or not file.filename:
        return jsonify({"error": "فایلی انتخاب نشده است"}), 400
    if not file.filename.lower().endswith(".xlsx"):
        return jsonify({"error": "فقط فایل اکسل (xlsx.) پذیرفته می‌شود"}), 400

    file_bytes = file.read()
    token = customer_import.save_pending_import(file_bytes)
    conflicts = customer_import.find_duplicate_conflicts(file_bytes)
    return jsonify({"token": token, "conflicts": conflicts})


@bp.route("/import/start", methods=["POST"])
@login_required
@require("customer_account")
def import_start():
    data = request.get_json(silent=True) or {}
    token = data.get("token")
    skip_rows = {r for r in (data.get("skip_rows") or []) if isinstance(r, int)}
    if not customer_import.is_valid_token(token):
        return jsonify({"error": "نشست آپلود پیدا نشد — لطفاً دوباره فایل را انتخاب کنید"}), 400

    file_bytes = customer_import.load_pending_import(token)
    if file_bytes is None:
        return jsonify({"error": "فایل موقت پیدا نشد — لطفاً دوباره فایل را انتخاب کنید"}), 400

    job_id = customer_import.start_import_job(file_bytes, current_user.id, skip_rows=skip_rows)
    customer_import.delete_pending_import(token)
    return jsonify({"job_id": job_id})


@bp.route("/import/cancel", methods=["POST"])
@login_required
@require("customer_account")
def import_cancel():
    """انصراف بعد از دیدن ردیف‌های تکراری: فایل موقت (که اطلاعات مشتری داره) روی سرور نمی‌مونه."""
    data = request.get_json(silent=True) or {}
    token = data.get("token")
    if customer_import.is_valid_token(token):
        customer_import.delete_pending_import(token)
    return jsonify({"ok": True})


@bp.route("/import/status/<int:job_id>")
@login_required
@require("customer_account")
def import_status(job_id):
    job = db.session.get(ImportJob, job_id) or abort(404)
    return jsonify({
        "status": job.status,
        "total": job.total_rows,
        "processed": job.processed_rows,
        "created": job.created_count,
        "skipped": job.skipped_count,
        "errors": job.errors or [],
        "error_message": job.error_message,
    })
