"""
Auto-log shop transactions into the daily diary (روزنامچه).
"""
from datetime import datetime, time
from decimal import Decimal

from django.utils import timezone

from shop_management.models.data.daily_diary import DailyDiaryLog


def _currency_label(currency):
    if currency is None:
        return ""
    title = str(getattr(currency, "title", "") or "").strip().upper()
    if title in {"AFG", "AFN", "AFGHANI"}:
        return "AFG"
    return getattr(currency, "title", "") or ""


def _decimal(value):
    if value is None:
        return None
    try:
        return Decimal(str(value))
    except Exception:
        return None


def _entry_datetime_from_date(date_value):
    if date_value is None:
        return timezone.now()
    if hasattr(date_value, "hour"):
        dt = date_value
        if timezone.is_naive(dt):
            return timezone.make_aware(dt, timezone.get_current_timezone())
        return dt
    return timezone.make_aware(
        datetime.combine(date_value, time.min),
        timezone.get_current_timezone(),
    )


def upsert_auto_diary_log(
    *,
    shop,
    entry_date,
    activity_type,
    source_model,
    source_id,
    title,
    party="",
    amount=None,
    currency=None,
    description="",
    entry_datetime=None,
    created_by=None,
    is_deleted=False,
    category=None,
):
    if not shop or not source_model or not source_id:
        return None

    if category is None:
        category = DailyDiaryLog.ACTIVITY_CATEGORY.get(
            activity_type, DailyDiaryLog.Category.MANUAL
        )

    dt = entry_datetime or _entry_datetime_from_date(entry_date)
    amount_val = _decimal(amount)

    existing = DailyDiaryLog.objects.filter(
        source_model=source_model,
        source_id=source_id,
        is_auto=True,
    ).first()

    if existing:
        existing.shop = shop
        existing.entry_date = entry_date or existing.entry_date
        existing.entry_datetime = dt
        existing.activity_type = activity_type
        existing.category = category
        existing.title = title
        existing.party = party or ""
        existing.amount = amount_val
        existing.currency = currency
        existing.description = description or ""
        existing.is_deleted = is_deleted
        if created_by and not existing.created_by_id:
            existing.created_by = created_by
        existing.save()
        return existing

    if is_deleted:
        return None

    return DailyDiaryLog.objects.create(
        shop=shop,
        entry_date=entry_date or timezone.localdate(),
        entry_datetime=dt,
        activity_type=activity_type,
        category=category,
        source_model=source_model,
        source_id=source_id,
        title=title,
        party=party or "",
        amount=amount_val,
        currency=currency,
        description=description or "",
        is_auto=True,
        is_deleted=False,
        created_by=created_by,
    )


def soft_delete_auto_log(source_model, source_id):
    DailyDiaryLog.objects.filter(
        source_model=source_model,
        source_id=source_id,
        is_auto=True,
    ).update(is_deleted=True)


def log_purchase(purchase, created_by=None):
    if not purchase or not purchase.shop_id:
        return
    if getattr(purchase, "is_deleted", False):
        soft_delete_auto_log("purchase", purchase.id)
        return
    amount = purchase.total_price
    if isinstance(amount, dict):
        amount = sum(Decimal(str(v)) for v in amount.values())
    upsert_auto_diary_log(
        shop=purchase.shop,
        entry_date=purchase.purchase_date,
        activity_type=DailyDiaryLog.ActivityType.PURCHASE,
        source_model="purchase",
        source_id=purchase.id,
        title=f"Purchase #{purchase.id}",
        party=purchase.supplier.name if purchase.supplier else "",
        amount=amount,
        currency=purchase.currency,
        description=purchase.notes or "" if hasattr(purchase, "notes") else "",
        entry_datetime=_entry_datetime_from_date(purchase.purchase_date),
        created_by=created_by or purchase.created_by,
    )


def log_sale(sale, created_by=None):
    if not sale or not sale.shop_id:
        return
    amount = sale.effective_total_amount
    if isinstance(amount, dict):
        amount = sum(Decimal(str(v)) for v in amount.values())
    party = sale.customer.name if sale.customer else (sale.general_customer_name or "Walk-in")
    upsert_auto_diary_log(
        shop=sale.shop,
        entry_date=sale.sale_date,
        activity_type=DailyDiaryLog.ActivityType.SALE,
        source_model="sale",
        source_id=sale.id,
        title=f"Sale #{sale.id}",
        party=party,
        amount=amount,
        currency=sale.currency,
        description=sale.notes or "",
        entry_datetime=_entry_datetime_from_date(sale.sale_date),
        created_by=created_by or sale.created_by,
    )


def log_quotation(quotation, created_by=None):
    if not quotation or not quotation.shop_id:
        return
    if getattr(quotation, "is_deleted", False):
        soft_delete_auto_log("quotation", quotation.id)
        return
    party = (
        quotation.customer.name if quotation.customer
        else (quotation.visitor_name or "Visitor")
    )
    upsert_auto_diary_log(
        shop=quotation.shop,
        entry_date=quotation.quotation_date,
        activity_type=DailyDiaryLog.ActivityType.QUOTATION,
        source_model="quotation",
        source_id=quotation.id,
        title=f"Quotation #{quotation.id}",
        party=party,
        amount=quotation.effective_total_amount,
        currency=quotation.currency,
        description=quotation.notes or "",
        entry_datetime=_entry_datetime_from_date(quotation.quotation_date),
        created_by=created_by or quotation.created_by,
    )


def log_sale_payment(payment, created_by=None):
    if not payment or not payment.sale_id:
        return
    sale = payment.sale
    upsert_auto_diary_log(
        shop=sale.shop,
        entry_date=timezone.localtime(payment.payment_date).date(),
        activity_type=DailyDiaryLog.ActivityType.SALE_PAYMENT,
        source_model="sale_payment",
        source_id=payment.id,
        title=f"Receivable — Sale #{sale.id} Payment",
        party=sale.customer.name if sale.customer else "Customer",
        amount=payment.amount,
        currency=payment.currency,
        description=payment.notes or "",
        entry_datetime=payment.payment_date,
        created_by=created_by or payment.created_by,
    )


def log_purchase_payment(payment, created_by=None):
    if not payment or not payment.purchase_id:
        return
    purchase = payment.purchase
    if getattr(purchase, "is_deleted", False):
        return
    upsert_auto_diary_log(
        shop=purchase.shop,
        entry_date=timezone.localtime(payment.payment_date).date(),
        activity_type=DailyDiaryLog.ActivityType.PURCHASE_PAYMENT,
        source_model="purchase_payment",
        source_id=payment.id,
        title=f"Payable — Purchase #{purchase.id} Payment",
        party=purchase.supplier.name if purchase.supplier else "Supplier",
        amount=payment.amount,
        currency=payment.currency,
        description=payment.notes or "",
        entry_datetime=payment.payment_date,
        created_by=created_by or payment.created_by,
    )


def log_customer_balance_payment(payment, created_by=None):
    if not payment:
        return
    shop = payment.customer_branch or payment.supplier_branch
    if not shop:
        return
    upsert_auto_diary_log(
        shop=shop,
        entry_date=timezone.localtime(payment.payment_date).date(),
        activity_type=DailyDiaryLog.ActivityType.CUSTOMER_PAYMENT,
        source_model="customer_balance_payment",
        source_id=payment.id,
        title=f"Receivable — Customer Balance #{payment.id}",
        party=payment.customer.name if payment.customer else "Customer",
        amount=payment.amount,
        currency=payment.currency,
        description=payment.notes or "",
        entry_datetime=payment.payment_date,
        created_by=created_by or payment.created_by,
    )


def log_supplier_balance_payment(payment, created_by=None):
    if not payment or not payment.supplier_id:
        return
    shop = payment.supplier.shop
    upsert_auto_diary_log(
        shop=shop,
        entry_date=timezone.localtime(payment.payment_date).date(),
        activity_type=DailyDiaryLog.ActivityType.SUPPLIER_PAYMENT,
        source_model="supplier_balance_payment",
        source_id=payment.id,
        title=f"Payable — Supplier Balance #{payment.id}",
        party=payment.supplier.name if payment.supplier else "Supplier",
        amount=payment.amount,
        currency=payment.currency,
        description=payment.notes or "",
        entry_datetime=payment.payment_date,
        created_by=created_by or payment.created_by,
    )


def log_stock_transfer(transfer, created_by=None):
    if not transfer:
        return
    shop = transfer.customer_branch or transfer.supplier_branch
    if not shop:
        return
    amount = _decimal(transfer.sale_price or 0)
    if amount is not None and transfer.transfer_quantity:
        amount *= Decimal(transfer.transfer_quantity)
    upsert_auto_diary_log(
        shop=shop,
        entry_date=transfer.transfer_date,
        activity_type=DailyDiaryLog.ActivityType.STOCK_TRANSFER,
        source_model="stock_transfer",
        source_id=transfer.id,
        title=transfer.transfer_id or f"Stock Transfer #{transfer.id}",
        party=f"{transfer.supplier_branch.name} → {transfer.customer_branch.name}",
        amount=amount,
        currency=transfer.currency,
        description=transfer.notes or "",
        entry_datetime=transfer.transfer_datetime or _entry_datetime_from_date(transfer.transfer_date),
        created_by=created_by or transfer.created_by,
    )


def log_expense(expense, created_by=None):
    if not expense or not expense.shop_id:
        return
    upsert_auto_diary_log(
        shop=expense.shop,
        entry_date=timezone.localtime(expense.payment_date).date(),
        activity_type=DailyDiaryLog.ActivityType.EXPENSE,
        source_model="expense",
        source_id=expense.id,
        title=f"Expense #{expense.id}",
        party=expense.expense_by or "",
        amount=expense.amount,
        currency=expense.currency,
        description=expense.notes or "",
        entry_datetime=expense.payment_date,
        created_by=created_by or expense.created_by,
    )


def log_sale_return(sale_return, created_by=None):
    if not sale_return or not sale_return.shop_id:
        return
    upsert_auto_diary_log(
        shop=sale_return.shop,
        entry_date=timezone.localtime(sale_return.return_date).date(),
        activity_type=DailyDiaryLog.ActivityType.SALE_RETURN,
        source_model="sale_return",
        source_id=sale_return.id,
        title=f"Sale Return #{sale_return.id}",
        party=f"Sale #{sale_return.sale_id}" if sale_return.sale_id else "",
        amount=sale_return.total_refound_amount,
        currency=sale_return.sale.currency if sale_return.sale else None,
        description=sale_return.reason or "",
        entry_datetime=sale_return.return_date,
        created_by=created_by or sale_return.created_by,
    )


def log_purchase_return(purchase_return, created_by=None):
    if not purchase_return or not purchase_return.shop_id:
        return
    upsert_auto_diary_log(
        shop=purchase_return.shop,
        entry_date=timezone.localtime(purchase_return.return_date).date(),
        activity_type=DailyDiaryLog.ActivityType.PURCHASE_RETURN,
        source_model="purchase_return",
        source_id=purchase_return.id,
        title=f"Purchase Return #{purchase_return.id}",
        party=f"Purchase #{purchase_return.purchase_id}" if purchase_return.purchase_id else "",
        amount=purchase_return.total_refund_amount,
        currency=purchase_return.purchase.currency if purchase_return.purchase else None,
        description=purchase_return.reason or "",
        entry_datetime=purchase_return.return_date,
        created_by=created_by or purchase_return.created_by,
    )


def log_payroll(payroll, created_by=None):
    if not payroll or not payroll.shop_id:
        return
    upsert_auto_diary_log(
        shop=payroll.shop,
        entry_date=timezone.localtime(payroll.payment_date).date(),
        activity_type=DailyDiaryLog.ActivityType.PAYROLL,
        source_model="payroll",
        source_id=payroll.id,
        title=f"Payroll #{payroll.id}",
        party=payroll.employee.name if payroll.employee else "",
        amount=payroll.amount,
        currency=payroll.currency,
        description=payroll.notes or "",
        entry_datetime=payroll.payment_date,
        created_by=created_by or payroll.created_by,
    )


def log_advance(advance, created_by=None):
    if not advance or not advance.shop_id:
        return
    payment_date = advance.payment_date or timezone.now()
    upsert_auto_diary_log(
        shop=advance.shop,
        entry_date=timezone.localtime(payment_date).date(),
        activity_type=DailyDiaryLog.ActivityType.ADVANCE,
        source_model="advance",
        source_id=advance.id,
        title=f"Advance #{advance.id}",
        party=advance.employee.name if advance.employee else "",
        amount=advance.amount,
        currency=advance.currency,
        description=advance.notes or "",
        entry_datetime=payment_date,
        created_by=created_by or advance.created_by,
    )


def log_passive_income(income, created_by=None):
    if not income or not income.shop_id:
        return
    upsert_auto_diary_log(
        shop=income.shop,
        entry_date=timezone.localtime(income.income_date).date(),
        activity_type=DailyDiaryLog.ActivityType.PASSIVE_INCOME,
        source_model="passive_income",
        source_id=income.id,
        title=f"Receivable — Passive Income #{income.id}",
        party=income.name or "",
        amount=income.amount,
        currency=income.currency,
        description=income.notes or "",
        entry_datetime=income.income_date,
        created_by=created_by or income.created_by,
    )


def log_customer_debtor(debtor, created_by=None):
    if not debtor or not debtor.customer_id:
        return
    shop = debtor.customer.shop
    upsert_auto_diary_log(
        shop=shop,
        entry_date=timezone.localtime(debtor.loan_date).date(),
        activity_type=DailyDiaryLog.ActivityType.CUSTOMER_DEBTOR,
        source_model="customer_debtor",
        source_id=debtor.id,
        title=f"Payable — Loan #{debtor.id}",
        party=debtor.customer.name if debtor.customer else "Customer",
        amount=debtor.amount,
        currency=debtor.currency,
        description=debtor.notes or "",
        entry_datetime=debtor.loan_date,
        created_by=created_by or debtor.created_by,
    )


def log_account_transfer(transfer, created_by=None):
    if not transfer or not transfer.from_account_id:
        return
    shop = transfer.from_account.shop
    upsert_auto_diary_log(
        shop=shop,
        entry_date=transfer.transfer_date,
        activity_type=DailyDiaryLog.ActivityType.ACCOUNT_TRANSFER,
        source_model="account_transfer",
        source_id=transfer.id,
        title=f"Payable — Account Transfer #{transfer.id}",
        party=f"{transfer.from_account.name} → {transfer.to_account.name}",
        amount=transfer.amount,
        currency=transfer.from_currency,
        description=transfer.note or transfer.reference or "",
        entry_datetime=_entry_datetime_from_date(transfer.transfer_date),
        created_by=created_by,
    )
