import uuid
from datetime import datetime
from app.extensions import db
from app.models.master import SavingsPlanMember
from app.models.transactions import LoanRequest, Loan, Debit
from app.services.notification_service import create_notification
from app.services.email_service import send_templated_email
from app.services.allocation_service import CashierAllocation, parse_allocations
from app.services.cashier_balance_service import get_cashier_balance_map
from app.utils.id_utils import generate_debit_transaction_id


def check_loan_eligibility(user):
    if not user:
        return {"isActive": False}

    savings_member = SavingsPlanMember.query.filter_by(user_id=user.id, status="active").first()
    if not savings_member:
        return {
            "isActive": True,
            "isEligible": False,
            "message": "Must be an ACTIVE member in the savings plan to request loans.",
        }

    if not savings_member.member_id:
        return {
            "isActive": True,
            "isEligible": False,
            "message": "Invalid membership details. Please contact support.",
        }

    active_loan = Loan.query.filter_by(member_id=savings_member.member_id, status="active").first()
    if active_loan:
        return {"isActive": True, "isEligible": False, "message": "You already have an active loan."}

    pending_request = LoanRequest.query.filter(
        LoanRequest.member_id == savings_member.member_id,
        LoanRequest.status.in_(["pending", "approved"]),
    ).first()

    if pending_request:
        return {
            "isActive": True,
            "isEligible": False,
            "message": f"You already have a loan request with status: {pending_request.status.upper()}",
        }

    return {"isActive": True, "isEligible": True, "message": "You are eligible to request a loan."}


def create_loan_request(user, data, document_id=None):
    savings_member = SavingsPlanMember.query.filter_by(user_id=user.id, status="active").first()
    if not savings_member:
        return None, "Must be enrolled in an active savings plan to request loans"

    member_id = savings_member.member_id

    active_loan = Loan.query.filter_by(member_id=member_id, status="active").first()
    if active_loan:
        return None, "You already have an active loan"

    pending_request = LoanRequest.query.filter(
        LoanRequest.member_id == member_id,
        LoanRequest.status.in_(["pending", "approved"]),
    ).first()
    if pending_request:
        return None, f"You already have a loan request with status: {pending_request.status.upper()}"

    loan_request = LoanRequest(
        request_id=f"LR{uuid.uuid4().hex[:8].upper()}",
        member_id=member_id,
        amount=float(data["amount"]),
        reason=data["reason"],
        term_months=int(data["term_months"]),
        document_id=document_id,
        status="pending",
        created_at=datetime.utcnow(),
    )

    db.session.add(loan_request)
    db.session.commit()

    send_templated_email(
        to=user.email,
        subject="Loan Request Submitted",
        template="emails/loan_request_submitted.html",
        cc_admin=True,
        request_id=loan_request.request_id,
        amount=loan_request.amount,
        member_name=user.full_name,
        term_months=loan_request.term_months,
    )

    create_notification(
        user_id=user.id,
        title="Loan request submitted",
        message=f"Loan request {loan_request.request_id} submitted.",
        notif_type="info",
        link="/app/requests",
    )

    return loan_request, None


def process_loan_request(request_obj, status, cashier_id=None, allocations=None, force: bool = False):
    member = request_obj.member
    user = member.user if member else None

    if status == "approved":
        if request_obj.status in ["released"] and not force:
            raise ValueError("Request already released.")

        request_obj.status = "approved"
        request_obj.cashier_id = None
        db.session.commit()

        send_templated_email(
            to=user.email,
            subject="Loan Request Approved",
            template="emails/loan_request_approved.html",
            cc_admin=True,
            request_id=request_obj.request_id,
            member_name=user.full_name,
        )
        create_notification(
            user_id=user.id,
            title="Loan request approved",
            message=f"Loan request {request_obj.request_id} approved.",
            notif_type="success",
            link="/app/requests",
        )
        return request_obj

    if status == "released":
        if request_obj.status == "released":
            if not force:
                raise ValueError("Request already released.")
            if allocations:
                payload = {"allocations": allocations}
                parsed_allocations, err = parse_allocations(payload, total_required=float(request_obj.amount or 0))
                if err:
                    raise ValueError(err)
                if parsed_allocations:
                    request_obj.cashier_id = int(parsed_allocations[0].cashier_id)
                    db.session.commit()
            return request_obj

        if request_obj.status != "approved":
            raise ValueError("Only an approved request can be released.")

        payload = {"allocations": allocations} if allocations is not None else {}
        parsed_allocations, err = parse_allocations(payload, total_required=float(request_obj.amount or 0))
        if err:
            raise ValueError(err)

        if not parsed_allocations:
            raise ValueError("Disbursement allocation is required to release loan funds.")

        # validate cashier balances
        balance_map = get_cashier_balance_map(active_members_only=True)
        for alloc in parsed_allocations:
            if float(balance_map.get(int(alloc.cashier_id), 0.0)) + 1e-9 < float(alloc.amount):
                raise ValueError(f"Insufficient cashier balance for cashier_id={alloc.cashier_id}.")

        # Create loan if not exists
        existing_loan = Loan.query.filter_by(request_id=request_obj.request_id).first()
        if existing_loan:
            if not force:
                raise ValueError("Loan already created for this request.")
            request_obj.status = "released"
            request_obj.cashier_id = int(parsed_allocations[0].cashier_id)
            db.session.commit()
            return request_obj

        loan_id = f"LO{request_obj.request_id}"
        if Loan.query.filter_by(loan_id=loan_id).first():
            loan_id = f"{loan_id}{uuid.uuid4().hex[:2].upper()}"

        loan = Loan(
            loan_id=loan_id,
            member_id=request_obj.member_id,
            request_id=request_obj.request_id,
            amount=float(request_obj.amount),
            term_months=int(request_obj.term_months),
            status="active",
            created_at=datetime.utcnow(),
            disbursement_date=datetime.utcnow(),
        )
        db.session.add(loan)

        # store first cashier for backwards compatibility
        request_obj.cashier_id = int(parsed_allocations[0].cashier_id)
        request_obj.status = "released"

        for alloc in parsed_allocations:
            debit = Debit(
                transaction_id=generate_debit_transaction_id("TD"),
                member_id=request_obj.member_id,
                transaction_type="loan_disbursement",
                amount=float(alloc.amount),
                cashier_id=int(alloc.cashier_id),
                loan_id=loan.loan_id,
                transaction_date=datetime.utcnow().date(),
                notes=f"Loan disbursement for {request_obj.request_id}",
            )
            db.session.add(debit)

        db.session.commit()

        send_templated_email(
            to=user.email,
            subject="Loan Funds Released",
            template="emails/loan_funds_released.html",
            cc_admin=True,
            request_id=request_obj.request_id,
            member_name=user.full_name,
            amount=request_obj.amount,
        )
        create_notification(
            user_id=user.id,
            title="Loan funds released",
            message=f"Loan {loan.loan_id} funds released.",
            notif_type="info",
            link="/app/requests",
        )
        return request_obj

    if status == "rejected":
        request_obj.status = "rejected"
        request_obj.cashier_id = None
        db.session.commit()
        send_templated_email(
            to=user.email,
            subject="Loan Request Rejected",
            template="emails/loan_request_rejected.html",
            cc_admin=True,
            request_id=request_obj.request_id,
            member_name=user.full_name,
        )
        create_notification(
            user_id=user.id,
            title="Loan request rejected",
            message=f"Loan request {request_obj.request_id} rejected.",
            notif_type="error",
            link="/app/requests",
        )
        return request_obj

    if status == "pending":
        request_obj.status = "pending"
        request_obj.cashier_id = None
        db.session.commit()
        return request_obj

    raise ValueError("Invalid status transition.")
