from datetime import date
import uuid
from app.extensions import db
from app.models.transactions import ReleaseRequest, Credit, Debit, Loan
from app.services.allocation_service import CashierAllocation, parse_allocations
from app.services.cashier_balance_service import get_cashier_balance_map
from app.services.email_service import send_templated_email
from app.services.notification_service import create_notification
from app.utils.id_utils import generate_credit_transaction_id


def process_release(release_request, action, cashier_id=None, notes=None, allocations=None, force: bool = False):
    member = release_request.member
    user = member.user if member else None

    if action == "reject":
        release_request.status = "rejected"
        release_request.notes = notes
        db.session.commit()
        send_templated_email(
            to=user.email,
            subject="Release Request Rejected",
            template="emails/release_request_rejected.html",
            cc_admin=True,
            request_id=release_request.release_request_id,
            amount=release_request.release_amount,
            member_name=user.full_name,
        )
        create_notification(
            user_id=user.id,
            title="Release request rejected",
            message=f"Release request {release_request.release_request_id} rejected.",
            notif_type="error",
            link="/app/requests",
        )
        return release_request

    if action == "release":
        if release_request.status == "released":
            if not force:
                raise ValueError("Request already released.")
            # allow metadata edits only (no reposting)
            if notes is not None:
                release_request.notes = notes
            if cashier_id is not None:
                release_request.cashier_id = cashier_id
            db.session.commit()
            return release_request

        payload = {"allocations": allocations} if allocations is not None else {}
        parsed_allocations, err = parse_allocations(payload, total_required=float(release_request.release_amount or 0))
        if err:
            raise ValueError(err)
        if not parsed_allocations:
            if not cashier_id:
                raise ValueError("Cashier is required to process release.")
            parsed_allocations = [CashierAllocation(cashier_id=int(cashier_id), amount=float(release_request.release_amount or 0))]

        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}.")

        release_request.status = "released"
        release_request.cashier_id = int(parsed_allocations[0].cashier_id)
        release_request.notes = notes

        active_loan = Loan.query.filter_by(member_id=member.member_id, status="active").first()
        if active_loan and release_request.outstanding_loan > 0:
            loan_payment = Credit(
                transaction_id=generate_credit_transaction_id("LP"),
                member_id=member.member_id,
                transaction_type="emi",
                total_amount=release_request.outstanding_loan,
                loan_id=active_loan.loan_id,
                cashier_id=int(parsed_allocations[0].cashier_id),
                release_request_id=release_request.release_request_id,
                transaction_date=date.today(),
            )
            db.session.add(loan_payment)

            total_paid = sum(
                credit.total_amount for credit in Credit.query.filter_by(loan_id=active_loan.loan_id).all()
            ) + release_request.outstanding_loan
            if total_paid >= active_loan.amount:
                active_loan.status = "closed"

        if release_request.release_amount > 0 and not Debit.query.filter_by(
            release_request_id=release_request.release_request_id, transaction_type="savings_release"
        ).first():
            for alloc in parsed_allocations:
                savings_release = Debit(
                    transaction_id=f"SR-{uuid.uuid4().hex[:8].upper()}",
                    member_id=member.member_id,
                    transaction_type="savings_release",
                    amount=float(alloc.amount),
                    cashier_id=int(alloc.cashier_id),
                    release_request_id=release_request.release_request_id,
                    transaction_date=date.today(),
                    notes=f"Savings release for request {release_request.release_request_id}",
                )
                db.session.add(savings_release)

        member.status = "inactive"
        member.end_date = date.today()

        db.session.commit()

        send_templated_email(
            to=user.email,
            subject="Release Amount Processed",
            template="emails/request_released.html",
            cc_admin=True,
            request_id=release_request.release_request_id,
            amount=release_request.release_amount,
            member_name=user.full_name,
        )
        create_notification(
            user_id=user.id,
            title="Release processed",
            message=f"Release request {release_request.release_request_id} processed.",
            notif_type="success",
            link="/app/requests",
        )

    return release_request
