import random
import string
from datetime import datetime
from app.extensions import db
from app.models.transactions import CharityRequest, Debit
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_debit_transaction_id


def generate_charity_id():
    prefix = "CH"
    timestamp = datetime.now().strftime("%y%m%d")
    random_chars = "".join(random.choices(string.ascii_uppercase + string.digits, k=4))
    return f"{prefix}{timestamp}{random_chars}"


def update_charity_request(charity_request, status, allotted_amount=None, cashier_id=None, allocations=None, force: bool = False):
    charity_request.status = status
    if allotted_amount is not None:
        charity_request.allotted_amount = allotted_amount
    if cashier_id is not None:
        charity_request.cashier_id = cashier_id

    member = charity_request.member
    user = member.user if member else None

    if status in ["approved", "rejected"]:
        db.session.commit()
        send_templated_email(
            to=user.email,
            subject="Charity Request Status Update",
            template="emails/charity_status_changed.html",
            cc_admin=True,
            charity_id=charity_request.charity_id,
            member_name=user.full_name,
            status=status,
            beneficiary=charity_request.beneficiary_name,
        )
        create_notification(
            user_id=user.id,
            title="Charity request updated",
            message=f"Charity request {charity_request.charity_id} {status}.",
            notif_type="info" if status == "approved" else "error",
            link="/app/requests",
        )

    if status == "released" and (allotted_amount or 0) > 0:
        if allocations is not None and len(allocations) > 0:
            # store first cashier for backwards compatibility
            try:
                charity_request.cashier_id = int(allocations[0].get("cashier_id"))
            except Exception:
                pass

        if Debit.query.filter_by(charity_id=charity_request.charity_id, transaction_type="charity").first():
            if not force:
                raise ValueError("Charity already released.")
            db.session.commit()
            return charity_request

        payload = {"allocations": allocations} if allocations is not None else {}
        parsed_allocations, err = parse_allocations(payload, total_required=float(allotted_amount or 0))
        if err:
            raise ValueError(err)
        if not parsed_allocations:
            if not cashier_id:
                raise ValueError("Cashier is required to release charity funds.")
            parsed_allocations = [CashierAllocation(cashier_id=int(cashier_id), amount=float(allotted_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}.")

        for alloc in parsed_allocations:
            debit = Debit(
                transaction_id=generate_debit_transaction_id("TD"),
                member_id=charity_request.member_id,
                transaction_type="charity",
                amount=float(alloc.amount),
                charity_id=charity_request.charity_id,
                cashier_id=int(alloc.cashier_id),
                transaction_date=datetime.now().date(),
                notes=f"Charity payment for {charity_request.charity_id}",
            )
            db.session.add(debit)
        db.session.commit()

        send_templated_email(
            to=user.email,
            subject="Charity Funds Released",
            template="emails/charity_fund_released.html",
            cc_admin=True,
            charity_id=charity_request.charity_id,
            beneficiary=charity_request.beneficiary_name,
            requested_amount=charity_request.amount,
            allotted_amount=allotted_amount,
            member_name=user.full_name,
        )
        create_notification(
            user_id=user.id,
            title="Charity funds released",
            message=f"Charity {charity_request.charity_id} released.",
            notif_type="success",
            link="/app/requests",
        )

    db.session.commit()
    return charity_request
