from flask import request
from datetime import datetime
from flask_jwt_extended import jwt_required, get_jwt_identity
from sqlalchemy import func, desc
from app.api import api_bp
from app.extensions import db
from app.models.transactions import Credit
from app.models.master import SavingsPlanMember, Cashier
from app.models.user import User
from app.services.credit_service import add_credit, notify_credit_member
from app.utils.auth import role_required, permission_required
from app.utils.pagination import paginate
from app.utils.response import ok, fail


def _ensure_editor_cashier_access(user: User, cashier_id: int | None):
    if not user or user.access_level != "editor":
        return None
    cashier_ids = [c.id for c in Cashier.query.filter_by(user_id=user.id).all()]
    if not cashier_ids:
        return fail("No cashier assigned to your account", 403)
    if cashier_id is not None and cashier_id not in cashier_ids:
        return fail("You do not have access to this cashier", 403)
    return None


def _build_credit_query(search, transaction_type, member_ids=None):
    query = db.session.query(
        Credit,
        SavingsPlanMember.member_short_name,
        User.username,
        Cashier.name.label("cashier_name"),
    ).join(SavingsPlanMember, Credit.member_id == SavingsPlanMember.member_id)
    query = query.join(User, SavingsPlanMember.user_id == User.id)
    query = query.outerjoin(Cashier, Credit.cashier_id == Cashier.id)

    if member_ids:
        query = query.filter(Credit.member_id.in_(member_ids))

    if search:
        query = query.filter(
            (Credit.transaction_id.ilike(f"%{search}%"))
            | (Credit.member_id.ilike(f"%{search}%"))
            | (SavingsPlanMember.member_short_name.ilike(f"%{search}%"))
            | (Cashier.name.ilike(f"%{search}%"))
        )

    if transaction_type:
        query = query.filter(Credit.transaction_type == transaction_type)

    return query


@api_bp.post("/credits")
@permission_required("credits.create")
def create_credit():
    data = request.json or {}
    user_id = get_jwt_identity()
    user = User.query.get(user_id)
    cashier_id = None
    if user and user.cashiers:
        cashier_id = user.cashiers[0].id

    credit, error, loan_closed = add_credit(data, cashier_id=cashier_id, actor_user_id=user_id)
    if error:
        return fail(error, 400)

    member = SavingsPlanMember.query.filter_by(member_id=credit.member_id).first()
    if member:
        notify_credit_member(member.user, credit, data.get("transaction_type", credit.transaction_type))
        from app.services.notification_service import create_notification
        create_notification(
            user_id=member.user_id,
            title="Credit posted",
            message=f"Credit {credit.transaction_id} posted.",
            notif_type="success",
            link="/app/credits",
        )
        if data.get("transaction_type") == "emi" and loan_closed:
            from app.services.email_service import send_templated_email
            send_templated_email(
                to=member.user.email,
                subject="Loan Payment Completed",
                template="emails/loan_closed.html",
                cc_admin=True,
                loan_id=credit.loan_id,
                member_name=member.user.full_name,
                closure_date=datetime.now().strftime("%Y-%m-%d"),
            )

    return ok({"id": credit.id}, "Credit created", 201)


@api_bp.get("/credits")
@permission_required("credits.view")
def list_credits():
    user_id = get_jwt_identity()
    user = User.query.get(user_id)
    denied = _ensure_editor_cashier_access(user, None)
    if denied:
        return denied

    page = request.args.get("page", 1, type=int)
    page_size = request.args.get("page_size", 20, type=int)
    search = request.args.get("search", "")
    sort_by = request.args.get("sort", "created_at")
    order = request.args.get("order", "desc")

    transaction_type = request.args.get("transaction_type")
    query = _build_credit_query(search, transaction_type)
    if user and user.access_level == "editor":
        cashier_ids = [c.id for c in Cashier.query.filter_by(user_id=user.id).all()]
        query = query.filter(Credit.cashier_id.in_(cashier_ids))

    if sort_by in ["transaction_id", "member_id", "transaction_type", "total_amount", "transaction_date", "created_at"]:
        column = getattr(Credit, sort_by)
    elif sort_by == "cashier_name":
        column = Cashier.name
    elif sort_by == "member_short_name":
        column = SavingsPlanMember.member_short_name
    else:
        column = Credit.created_at

    query = query.order_by(desc(column)) if order == "desc" else query.order_by(column)

    paged = paginate(query, page, page_size)
    items = [
        {
            "id": credit.id,
            "transaction_id": credit.transaction_id,
            "member_id": credit.member_id,
            "member_short_name": member_short_name,
            "transaction_type": credit.transaction_type,
            "loan_id": credit.loan_id,
            "total_amount": float(credit.total_amount),
            "savings_amount": float(credit.savings_amount or 0),
            "charity_amount": float(credit.charity_amount or 0),
            "transaction_date": credit.transaction_date.strftime("%Y-%m-%d"),
            "created_at": credit.created_at.strftime("%Y-%m-%d %H:%M"),
            "cashier_name": cashier_name,
        }
        for credit, member_short_name, username, cashier_name in paged["items"]
    ]

    totalamt = query.with_entities(func.coalesce(func.sum(Credit.total_amount), 0)).scalar()
    charityamt = query.with_entities(func.coalesce(func.sum(Credit.charity_amount), 0)).scalar()
    savingamt = query.with_entities(func.coalesce(func.sum(Credit.savings_amount), 0)).filter(Credit.transaction_type == "savings").scalar()
    emiamt = query.with_entities(func.coalesce(func.sum(Credit.total_amount), 0)).filter(Credit.transaction_type == "emi").scalar()

    return ok(
        {
            "credits": items,
            "page": paged["page"],
            "pages": paged["pages"],
            "total": paged["total"],
            "page_size": paged["page_size"],
            "summary": {
                "totalAmount": float(totalamt),
                "savingsAmount": float(savingamt),
                "emiAmount": float(emiamt),
                "charityAmount": float(charityamt),
            },
        }
    )


@api_bp.get("/credits/my")
@jwt_required()
def list_my_credits():
    user_id = get_jwt_identity()
    member_ids = [m.member_id for m in SavingsPlanMember.query.filter_by(user_id=user_id).all()]
    if not member_ids:
        return ok({"credits": [], "page": 1, "pages": 0, "total": 0, "page_size": 20, "summary": {}})

    page = request.args.get("page", 1, type=int)
    page_size = request.args.get("page_size", 20, type=int)
    search = request.args.get("search", "")
    sort_by = request.args.get("sort", "created_at")
    order = request.args.get("order", "desc")
    transaction_type = request.args.get("transaction_type")

    query = _build_credit_query(search, transaction_type, member_ids)

    if sort_by in ["transaction_id", "member_id", "transaction_type", "total_amount", "transaction_date", "created_at"]:
        column = getattr(Credit, sort_by)
    elif sort_by == "cashier_name":
        column = Cashier.name
    elif sort_by == "member_short_name":
        column = SavingsPlanMember.member_short_name
    else:
        column = Credit.created_at

    query = query.order_by(desc(column)) if order == "desc" else query.order_by(column)

    paged = paginate(query, page, page_size)
    items = [
        {
            "id": credit.id,
            "transaction_id": credit.transaction_id,
            "member_id": credit.member_id,
            "member_short_name": member_short_name,
            "transaction_type": credit.transaction_type,
            "loan_id": credit.loan_id,
            "total_amount": float(credit.total_amount),
            "savings_amount": float(credit.savings_amount or 0),
            "charity_amount": float(credit.charity_amount or 0),
            "transaction_date": credit.transaction_date.strftime("%Y-%m-%d"),
            "created_at": credit.created_at.strftime("%Y-%m-%d %H:%M"),
            "cashier_name": cashier_name,
        }
        for credit, member_short_name, username, cashier_name in paged["items"]
    ]

    totalamt = query.with_entities(func.coalesce(func.sum(Credit.total_amount), 0)).scalar()
    charityamt = query.with_entities(func.coalesce(func.sum(Credit.charity_amount), 0)).scalar()
    savingamt = query.with_entities(func.coalesce(func.sum(Credit.savings_amount), 0)).filter(Credit.transaction_type == "savings").scalar()
    emiamt = query.with_entities(func.coalesce(func.sum(Credit.total_amount), 0)).filter(Credit.transaction_type == "emi").scalar()

    return ok(
        {
            "credits": items,
            "page": paged["page"],
            "pages": paged["pages"],
            "total": paged["total"],
            "page_size": paged["page_size"],
            "summary": {
                "totalAmount": float(totalamt),
                "savingsAmount": float(savingamt),
                "emiAmount": float(emiamt),
                "charityAmount": float(charityamt),
            },
        }
    )


@api_bp.get("/credits/<int:credit_id>")
@permission_required("credits.view")
def get_credit(credit_id):
    credit = Credit.query.get_or_404(credit_id)
    data = {
        "id": credit.id,
        "transaction_id": credit.transaction_id,
        "member_id": credit.member_id,
        "transaction_type": credit.transaction_type,
        "total_amount": float(credit.total_amount),
        "savings_amount": float(credit.savings_amount or 0),
        "charity_amount": float(credit.charity_amount or 0),
        "transaction_date": credit.transaction_date.strftime("%Y-%m-%d"),
        "cashier_id": credit.cashier_id,
    }
    return ok(data)


@api_bp.put("/credits/<int:credit_id>")
@permission_required("credits.update")
def update_credit(credit_id):
    user_id = get_jwt_identity()
    user = User.query.get(user_id)
    credit = Credit.query.get_or_404(credit_id)
    data = request.json or {}

    denied = _ensure_editor_cashier_access(user, credit.cashier_id)
    if denied:
        return denied

    credit.transaction_type = data.get("transaction_type", credit.transaction_type)
    credit.total_amount = data.get("total_amount", credit.total_amount)
    credit.savings_amount = data.get("savings_amount", credit.savings_amount)
    credit.charity_amount = data.get("charity_amount", credit.charity_amount)
    if data.get("cashier_id") is not None:
        denied = _ensure_editor_cashier_access(user, data.get("cashier_id"))
        if denied:
            return denied
        credit.cashier_id = data.get("cashier_id")
    if data.get("transaction_date"):
        credit.transaction_date = datetime.strptime(data["transaction_date"], "%Y-%m-%d").date()

    db.session.commit()
    return ok({"id": credit.id}, "Credit updated")


@api_bp.delete("/credits/<int:credit_id>")
@permission_required("credits.delete")
def delete_credit(credit_id):
    user_id = get_jwt_identity()
    user = User.query.get(user_id)
    credit = Credit.query.get_or_404(credit_id)
    denied = _ensure_editor_cashier_access(user, credit.cashier_id)
    if denied:
        return denied
    db.session.delete(credit)
    db.session.commit()
    return ok(message="Credit deleted")


@api_bp.post("/credits/bulk-delete")
@permission_required("credits.delete")
def bulk_delete_credits():
    data = request.json or {}
    selected_ids = data.get("selected_ids", [])
    if not selected_ids:
        return fail("No records selected for deletion", 400)
    count = 0
    for credit_id in selected_ids:
        credit = Credit.query.get(credit_id)
        if credit:
            db.session.delete(credit)
            count += 1
    db.session.commit()
    return ok({"count": count}, "Credits deleted")
