from datetime import timedelta

from flask import Blueprint, render_template, redirect, url_for, flash, request, abort, send_file
from flask_login import login_required, current_user
from sqlalchemy import func, and_

from app.extensions import db
from app.models.call import Call
from app.models.customer import Customer
from app.models.user import User
from app.models.tag import CallTag, Tag, TagCategory
from app.services.permissions import require
from app.services.activity_logger import log_action
from app.services import customer_service, tag_service, listing
from app.services.jalali import jalali_str_to_gregorian_date, to_jalali_str, iran_day_start_utc
from app.blueprints.calls.forms import CallLogForm

import io


bp = Blueprint("calls", __name__, url_prefix="/calls")


def _safe_next_url(target):
    """بعد از ثبت تماس، برگشت به همون صفحه‌ای که کاربر ازش اومده (مثلاً لیست کارها) —
    فقط آدرس داخل همین سایت پذیرفته می‌شه."""
    if not target or not target.startswith("/") or target.startswith("//") or "\\" in target:
        return None
    return target


# ============================================================
# لیست تماس‌ها
# ============================================================

@bp.route("/")
@login_required
@require("calls")
def index():

    search = request.args.get("q", "").strip()

    salesperson_id = request.args.get(
        "salesperson_id",
        type=int
    )

    answered_raw = request.args.get("answered")

    answered = {
        "1": True,
        "0": False
    }.get(answered_raw)

    date_from_raw = request.args.get(
        "date_from",
        ""
    ).strip()

    date_to_raw = request.args.get(
        "date_to",
        ""
    ).strip()

    # --------------------------------------------------------
    # تگ‌های انتخاب شده
    # --------------------------------------------------------

    tag_ids = [
        int(t)
        for t in request.args.getlist("tag_ids")
        if t.isdigit()
    ]

    # --------------------------------------------------------
    # آخرین تماس هر مشتری
    # --------------------------------------------------------

    latest_dates = (
        db.session.query(
            Call.customer_id.label("customer_id"),
            func.max(Call.call_date).label("max_date")
        )
        .group_by(Call.customer_id)
        .subquery()
    )

    latest_call = (
        db.session.query(
            Call.customer_id.label("customer_id"),
            Call.id.label("call_id"),
            Call.call_date.label("call_date"),
            Call.salesperson_id.label("salesperson_id"),
            Call.answered.label("answered"),
            Call.result_text.label("result_text"),
        )
        .join(
            latest_dates,
            and_(
                Call.customer_id == latest_dates.c.customer_id,
                Call.call_date == latest_dates.c.max_date,
            )
        )
    )

    # --------------------------------------------------------
    # فیلتر تگ
    #
    # اگر یک یا چند تگ انتخاب شده باشد،
    # فقط آخرین تماس‌هایی نمایش داده می‌شوند
    # که حداقل یکی از تگ‌های انتخاب‌شده را داشته باشند.
    # --------------------------------------------------------

    if tag_ids:

        latest_call = latest_call.filter(
            db.session.query(CallTag.id)
            .filter(
                CallTag.call_id == Call.id,
                CallTag.tag_id.in_(tag_ids)
            )
            .exists()
        )

    latest_call = latest_call.subquery()

    # --------------------------------------------------------
    # ساخت Query اصلی
    # --------------------------------------------------------

    query = (
        db.session.query(
            Customer,
            latest_call.c.call_id,
            latest_call.c.call_date,
            latest_call.c.salesperson_id,
            latest_call.c.answered,
            latest_call.c.result_text
        )
        .outerjoin(
            latest_call,
            latest_call.c.customer_id == Customer.id
        )
        .filter(
            Customer.is_deleted.is_(False)
        )
    )

    # --------------------------------------------------------
    # جستجو
    # --------------------------------------------------------

    if search:

        # نام، نام کامل، شماره (حتی با عدد فارسی) و کد مشتری
        query = query.filter(
            customer_service.search_condition(search)
        )

    # --------------------------------------------------------
    # فروشنده
    # --------------------------------------------------------

    if salesperson_id:

        query = query.filter(
            latest_call.c.salesperson_id == salesperson_id
        )

    # --------------------------------------------------------
    # وضعیت پاسخ
    # --------------------------------------------------------

    if answered is not None:

        query = query.filter(
            latest_call.c.answered.is_(answered)
        )

    # --------------------------------------------------------
    # تاریخ
    #
    # call_date به UTC ذخیره می‌شه؛ مرزهای روز به وقت ایران حساب
    # می‌شن و «تا تاریخ» کل همون روز رو هم شامل می‌شه.
    # --------------------------------------------------------

    try:

        if date_from_raw:

            query = query.filter(
                latest_call.c.call_date
                >= iran_day_start_utc(
                    jalali_str_to_gregorian_date(date_from_raw)
                )
            )

        if date_to_raw:

            query = query.filter(
                latest_call.c.call_date
                < iran_day_start_utc(
                    jalali_str_to_gregorian_date(date_to_raw)
                    + timedelta(days=1)
                )
            )

    except Exception:
        pass

    # --------------------------------------------------------
    # نتیجه (پیش‌فرض فقط ۵۰ ردیف؛ بقیه با «نمایش همه»)
    # --------------------------------------------------------

    total = query.order_by(None).count()

    query = query.order_by(
        latest_call.c.call_date.desc().nulls_last(),
        Customer.first_name,
        Customer.id.desc()
    )

    limit = listing.page_limit()

    if limit:
        query = query.limit(limit)

    rows = query.all()

    # --------------------------------------------------------
    # فروشندگان
    # --------------------------------------------------------

    salespeople = (
        User.query
        .order_by(User.full_name)
        .all()
    )

    salespeople_by_id = {
        u.id: u.full_name
        for u in salespeople
    }

    # --------------------------------------------------------
    # ثبت لاگ جستجو
    # --------------------------------------------------------

    if search:

        log_action(
            current_user,
            "search",
            "call",
            details={
                "query": search
            }
        )

    # --------------------------------------------------------
    # ارسال اطلاعات به Template
    # --------------------------------------------------------

    # --------------------------------------------------------
    # دسته‌بندی‌ها و تگ‌ها برای فیلتر تماس‌ها
    # --------------------------------------------------------

    tag_categories = (
        TagCategory.query
        .order_by(
            TagCategory.sort_order,
            TagCategory.id
        )
        .all()
    )
    return render_template(
        "calls/index.html",

        rows=rows,

        total=total,

        salespeople=salespeople,

        salespeople_by_id=salespeople_by_id,

        q=search,

        selected_salesperson=salesperson_id,

        selected_answered=answered_raw,

        date_from=date_from_raw,

        date_to=date_to_raw,

        # تگ‌ها
        tag_categories=tag_categories,

        selected_tag_ids=[
            str(t)
            for t in tag_ids
        ],
    )


# ============================================================
# ثبت تماس جدید
# ============================================================

@bp.route("/new", methods=["GET", "POST"])
@login_required
@require("calls")
def new():

    customer_id = (
        request.args.get(
            "customer_id",
            type=int
        )
        or request.form.get(
            "customer_id",
            type=int
        )
    )

    customer = (
        db.session.get(
            Customer,
            customer_id
        )
        if customer_id
        else None
    )

    form = CallLogForm()

    search_results = []

    if (
        not customer
        and request.method == "GET"
        and request.args.get("search")
    ):

        search_results = (
            customer_service.search_by_name(
                request.args.get("search")
            )
        )

    # --------------------------------------------------------
    # ثبت تماس
    # --------------------------------------------------------

    if form.validate_on_submit():

        target_customer = db.session.get(
            Customer,
            form.customer_id.data
        )

        if not target_customer:

            flash(
                "لطفاً یک مشتری برای این تماس انتخاب کنید",
                "error"
            )

        else:

            call = Call(
                customer_id=target_customer.id,
                salesperson_id=current_user.id,
                answered=(
                    form.answered.data == "1"
                ),
                result_text=(
                    form.result_text.data.strip()
                    if form.result_text.data
                    else None
                ),
            )

            db.session.add(call)

            db.session.flush()

            # ------------------------------------------------
            # ثبت تگ‌های تماس (فقط تگ‌هایی که واقعاً وجود دارن)
            # ------------------------------------------------

            requested_tag_ids = {
                int(tag_id)
                for tag_id in request.form.getlist("tag_ids")
                if tag_id.isdigit()
            }

            existing_tag_ids = {
                row[0]
                for row in db.session.query(Tag.id)
                .filter(Tag.id.in_(requested_tag_ids))
            } if requested_tag_ids else set()

            for tag_id in existing_tag_ids:

                db.session.add(
                    CallTag(
                        call_id=call.id,
                        tag_id=tag_id
                    )
                )

            db.session.commit()

            customer_service.refresh_statuses(
                [target_customer.id]
            )

            log_action(
                current_user,
                "create",
                "call",
                call.id,
                {
                    "customer_id": target_customer.id
                }
            )

            flash(
                "تماس با موفقیت ثبت شد",
                "success"
            )

            return redirect(
                _safe_next_url(request.values.get("next"))
                or url_for(
                    "customer_profile.show",
                    customer_id=target_customer.id
                )
            )

    return render_template(
        "calls/new.html",
        form=form,
        customer=customer,
        search_results=search_results,
        all_tag_groups=tag_service.all_tags_grouped(),
    )


# ============================================================
# جزئیات تماس
# ============================================================

@bp.route("/<int:call_id>")
@login_required
@require("calls")
def detail(call_id):

    call = (
        db.session.get(
            Call,
            call_id
        )
        or abort(404)
    )

    call_tags = (
        db.session.query(CallTag)
        .filter(
            CallTag.call_id == call_id
        )
        .all()
    )

    tag_names = [
        ct.tag.name
        for ct in call_tags
        if ct.tag
    ]

    return render_template(
        "calls/detail.html",
        call=call,
        tag_names=tag_names,
    )


# ============================================================
# حذف تماس - فقط مدیر
# ============================================================

@bp.route(
    "/<int:call_id>/delete",
    methods=["POST"]
)
@login_required
@require("calls")
def delete(call_id):

    if not current_user.is_admin():
        abort(403)

    call = (
        db.session.get(
            Call,
            call_id
        )
        or abort(404)
    )

    customer_id = call.customer_id

    # ثبت عملیات قبل از حذف

    log_action(
        current_user,
        "delete",
        "call",
        call.id,
        {
            "customer_id": customer_id,
            "call_date": (
                call.call_date.isoformat()
                if call.call_date
                else None
            )
        }
    )

    # حذف تماس

    db.session.delete(call)

    db.session.commit()

    customer_service.refresh_statuses(
        [customer_id]
    )

    flash(
        "تماس با موفقیت حذف شد",
        "success"
    )

    return redirect(
        url_for(
            "customer_profile.show",
            customer_id=customer_id
        )
    )


# ============================================================
# گزارش Excel تماس‌های یک مشتری
# ============================================================

@bp.route(
    "/customer/<int:customer_id>/report.xlsx"
)
@login_required
@require("calls")
def customer_calls_report(customer_id):

    customer = (
        db.session.get(
            Customer,
            customer_id
        )
        or abort(404)
    )

    # تمام تماس‌های این مشتری

    calls = (
        Call.query
        .filter(
            Call.customer_id == customer_id
        )
        .order_by(
            Call.call_date.desc()
        )
        .all()
    )

    # ساخت Excel

    from openpyxl import Workbook  # سنگینه و فقط همین‌جا لازمه؛ وقت بالا آمدن برنامه لود نشه

    workbook = Workbook()

    worksheet = workbook.active

    worksheet.title = "گزارش تماس‌ها"

    # عنوان

    worksheet["A1"] = "گزارش تماس‌های مشتری"

    worksheet["A2"] = "کد مشتری"

    worksheet["B2"] = (
        customer.customer_code
        or customer.id
    )

    worksheet["A3"] = "نام مشتری"

    worksheet["B3"] = (
        f"{customer.first_name or ''} "
        f"{customer.last_name or ''}"
    ).strip()

    worksheet["A4"] = "شماره موبایل"

    worksheet["B4"] = (
        customer.mobile_phone
        or "-"
    )

    worksheet["A5"] = "شماره ثابت"

    worksheet["B5"] = (
        customer.landline_phone
        or "-"
    )

    header_row = 7

    headers = [
        "ردیف",
        "تاریخ تماس",
        "فروشنده",
        "پاسخ داده شد؟",
        "نتیجه تماس",
        "تگ‌ها",
    ]

    for column, header in enumerate(
        headers,
        start=1
    ):

        worksheet.cell(
            row=header_row,
            column=column,
            value=header
        )

    # اطلاعات تماس‌ها

    for index, call in enumerate(
        calls,
        start=1
    ):

        row = header_row + index

        call_tags = (
            db.session.query(CallTag)
            .filter(
                CallTag.call_id == call.id
            )
            .all()
        )

        tag_names = [
            ct.tag.name
            for ct in call_tags
            if ct.tag
        ]

        if call.answered is True:

            answered_text = "بله"

        elif call.answered is False:

            answered_text = "خیر"

        else:

            answered_text = "-"

        worksheet.cell(
            row=row,
            column=1,
            value=index
        )

        worksheet.cell(
            row=row,
            column=2,
            value=(
                to_jalali_str(
                    call.call_date,
                    with_time=True
                )
                if call.call_date
                else "-"
            )
        )

        worksheet.cell(
            row=row,
            column=3,
            value=(
                call.salesperson.full_name
                if call.salesperson
                else "-"
            )
        )

        worksheet.cell(
            row=row,
            column=4,
            value=answered_text
        )

        worksheet.cell(
            row=row,
            column=5,
            value=call.result_text or "-"
        )

        worksheet.cell(
            row=row,
            column=6,
            value=(
                ", ".join(tag_names)
                if tag_names
                else "-"
            )
        )

    # عرض ستون‌ها

    worksheet.column_dimensions["A"].width = 10
    worksheet.column_dimensions["B"].width = 24
    worksheet.column_dimensions["C"].width = 25
    worksheet.column_dimensions["D"].width = 18
    worksheet.column_dimensions["E"].width = 60
    worksheet.column_dimensions["F"].width = 35

    # ذخیره در حافظه

    output = io.BytesIO()

    workbook.save(output)

    output.seek(0)

    customer_name = (
        f"{customer.first_name or ''}_"
        f"{customer.last_name or ''}"
    ).strip("_").replace(" ", "_")

    filename = (
        f"گزارش_تماس‌های_{customer_name}.xlsx"
    )

    return send_file(
        output,
        as_attachment=True,
        download_name=filename,
        mimetype=(
            "application/vnd.openxmlformats-officedocument"
            ".spreadsheetml.sheet"
        )
    )