"""
Services for automated daily user profit signals.
"""
from datetime import datetime, timedelta
from decimal import Decimal, ROUND_HALF_UP

from django.db import transaction
from django.utils import timezone

from wallet.models import Transaction, TransactionStatus, TransactionType, Wallet
from .models import (
    AutomatedSignalPlanStatus,
    AutomatedUserSignal,
    AutomatedUserSignalStatus,
    Coin,
    SignalProgramSettings,
    SignalType,
    UserSignalPlan,
)

MONEY_QUANTIZER = Decimal('0.01')
PRICE_QUANTIZER = Decimal('0.00000001')
DEFAULT_TARGET_MULTIPLIER = Decimal('2.00')
DEFAULT_COMPLETION_DAYS = 35
DEFAULT_SIGNAL_EXPIRY_MINUTES = 10
DEFAULT_TEST_INTERVAL_MINUTES = 5


def _quantize_money(value):
    """
    Normalize money values to 2 decimals.
    """
    return Decimal(value).quantize(MONEY_QUANTIZER, rounding=ROUND_HALF_UP)


def _quantize_price(value):
    """
    Normalize price values to 8 decimals.
    """
    return Decimal(value).quantize(PRICE_QUANTIZER, rounding=ROUND_HALF_UP)


def _combine_local_datetime(signal_date, signal_time):
    """
    Build a timezone-aware datetime for the configured local signal time.
    """
    naive_datetime = datetime.combine(signal_date, signal_time)
    return timezone.make_aware(naive_datetime, timezone.get_current_timezone())


def _next_release_after(moment, signal_time):
    """
    Return the next configured signal release datetime after a given moment.
    """
    local_moment = timezone.localtime(moment)
    release_today = _combine_local_datetime(local_moment.date(), signal_time)
    if local_moment < release_today:
        return release_today
    return _combine_local_datetime(local_moment.date() + timedelta(days=1), signal_time)


def _get_expiry_delta(settings):
    """
    Return the configured signal expiry duration.
    """
    expiry_minutes = max(settings.signal_expiry_minutes, 1)
    return timedelta(minutes=expiry_minutes)


def _is_test_mode_enabled(settings):
    """
    Return True when accelerated test mode is enabled.
    """
    return bool(settings.test_mode_enabled and settings.test_interval_minutes)


def _get_test_interval_delta(settings):
    """
    Return the accelerated test interval duration.
    """
    interval_minutes = max(settings.test_interval_minutes or DEFAULT_TEST_INTERVAL_MINUTES, 1)
    return timedelta(minutes=interval_minutes)


def _get_effective_expiry_delta(settings):
    """
    Return the effective signal expiry duration for the active schedule mode.
    """
    expiry_delta = _get_expiry_delta(settings)
    if _is_test_mode_enabled(settings):
        return min(expiry_delta, _get_test_interval_delta(settings))
    return expiry_delta


def _get_release_context(now, settings):
    """
    Return the current schedule slot details for automatic signal creation.
    """
    local_now = timezone.localtime(now)
    expiry_delta = _get_effective_expiry_delta(settings)

    if _is_test_mode_enabled(settings):
        interval_minutes = max(settings.test_interval_minutes or DEFAULT_TEST_INTERVAL_MINUTES, 1)
        slot_minute = (local_now.minute // interval_minutes) * interval_minutes
        scheduled_for = local_now.replace(minute=slot_minute, second=0, microsecond=0)
        expires_at = scheduled_for + expiry_delta
        next_release_at = scheduled_for + timedelta(minutes=interval_minutes)
        slot_key = scheduled_for.strftime('test-%Y%m%d%H%M')
        return {
            'signal_date': scheduled_for.date(),
            'slot_key': slot_key,
            'scheduled_for': scheduled_for,
            'expires_at': expires_at,
            'next_release_at': next_release_at,
        }

    signal_date = local_now.date()
    scheduled_for = _combine_local_datetime(signal_date, settings.daily_signal_time)
    expires_at = scheduled_for + expiry_delta
    return {
        'signal_date': signal_date,
        'slot_key': f'daily-{signal_date.isoformat()}',
        'scheduled_for': scheduled_for,
        'expires_at': expires_at,
        'next_release_at': _next_release_after(local_now, settings.daily_signal_time),
    }


def get_signal_program_settings():
    """
    Return the persisted signal settings or in-code defaults.
    """
    settings = SignalProgramSettings.objects.select_related('default_coin').order_by('id').first()
    if settings:
        return settings

    return SignalProgramSettings(
        is_enabled=True,
        target_multiplier=DEFAULT_TARGET_MULTIPLIER,
        completion_days=DEFAULT_COMPLETION_DAYS,
        signal_expiry_minutes=DEFAULT_SIGNAL_EXPIRY_MINUTES,
        test_mode_enabled=False,
        test_interval_minutes=DEFAULT_TEST_INTERVAL_MINUTES,
        show_dashboard_market_overview=True,
        dashboard_market_title='Trading Markets',
        telegram_channel_enabled=False,
        telegram_channel_title='Join Our Telegram Channel',
        telegram_channel_description='Get market updates, alerts, and announcements directly on Telegram.',
        telegram_button_text='Open Telegram',
        telegram_channel_url='',
        telegram_contact_username='',
        default_signal_type=SignalType.BUY,
    )


def _get_plan_target_multiplier(settings):
    """
    Return a safe multiplier for new plans.
    """
    multiplier = settings.target_multiplier or DEFAULT_TARGET_MULTIPLIER
    if multiplier <= Decimal('1.00'):
        return DEFAULT_TARGET_MULTIPLIER
    return multiplier


def _get_plan_completion_days(settings):
    """
    Return a safe completion day count for new plans.
    """
    return max(settings.completion_days or DEFAULT_COMPLETION_DAYS, 1)


def _get_signal_payload(user, signal_date, settings):
    """
    Return deterministic coin and pricing data for a user's daily signal.
    """
    if settings.default_coin and settings.default_coin.is_active:
        coin = settings.default_coin
    else:
        active_coins = list(Coin.objects.filter(is_active=True).order_by('id'))
        coin = None
        if active_coins:
            coin = active_coins[(user.id + signal_date.toordinal()) % len(active_coins)]

    signal_type = settings.default_signal_type or SignalType.BUY
    entry_price = _quantize_price((coin.current_price if coin else Decimal('0')) or Decimal('0'))

    if entry_price > 0:
        if signal_type == SignalType.BUY:
            target_price = _quantize_price(entry_price * Decimal('1.02000000'))
            stop_loss = _quantize_price(entry_price * Decimal('0.99000000'))
        else:
            target_price = _quantize_price(entry_price * Decimal('0.98000000'))
            stop_loss = _quantize_price(entry_price * Decimal('1.01000000'))
    else:
        target_price = Decimal('0')
        stop_loss = Decimal('0')

    return {
        'coin': coin,
        'signal_type': signal_type,
        'entry_price': entry_price,
        'target_price': target_price,
        'stop_loss': stop_loss,
    }


def _get_user_wallet_balance(user):
    """
    Return the user's current wallet balance.
    """
    wallet, created = Wallet.objects.get_or_create(user=user)
    return wallet.balance


def _get_first_release_for_plan(plan, settings):
    """
    Return the first signal release datetime for a plan.
    """
    if _is_test_mode_enabled(settings):
        started_at = timezone.localtime(plan.started_at)
        interval_minutes = max(settings.test_interval_minutes or DEFAULT_TEST_INTERVAL_MINUTES, 1)
        slot_minute = (started_at.minute // interval_minutes) * interval_minutes
        candidate = started_at.replace(minute=slot_minute, second=0, microsecond=0)
        if candidate < started_at:
            candidate += timedelta(minutes=interval_minutes)
        return candidate

    return _next_release_after(plan.started_at, settings.daily_signal_time)


def _mark_plan_completed_if_needed(plan, completed_at=None):
    """
    Mark a plan completed once all profit days are accepted.
    """
    if plan.status != AutomatedSignalPlanStatus.ACTIVE:
        return

    if plan.accepted_days < plan.completion_days and plan.credited_profit_amount < plan.target_profit_amount:
        return

    plan.accepted_days = min(plan.accepted_days, plan.completion_days)
    plan.credited_profit_amount = plan.target_profit_amount
    plan.status = AutomatedSignalPlanStatus.COMPLETED
    plan.completed_at = completed_at or timezone.now()
    plan.save(update_fields=[
        'accepted_days',
        'credited_profit_amount',
        'status',
        'completed_at',
        'updated_at',
    ])


def _get_signal_profit_amount_for_plan(plan):
    """
    Return the next profit amount to credit for a plan.
    """
    remaining_profit = _quantize_money(plan.target_profit_amount - plan.credited_profit_amount)
    if remaining_profit <= Decimal('0.00'):
        return Decimal('0.00')

    next_day_number = plan.accepted_days + 1
    if next_day_number >= plan.completion_days:
        return remaining_profit
    return min(plan.daily_profit_amount, remaining_profit)


def activate_signal_plan_for_approved_deposit(deposit):
    """
    Create or reset the automated signal plan for an approved deposit.
    """
    settings = get_signal_program_settings()
    target_multiplier = _get_plan_target_multiplier(settings)
    completion_days = _get_plan_completion_days(settings)
    principal_amount = _quantize_money(deposit.amount)
    target_amount = _quantize_money(principal_amount * target_multiplier)
    target_profit_amount = _quantize_money(target_amount - principal_amount)
    daily_profit_amount = _quantize_money(target_profit_amount / Decimal(completion_days))

    plan, created = UserSignalPlan.objects.get_or_create(
        deposit=deposit,
        defaults={
            'user': deposit.user,
            'principal_amount': principal_amount,
            'target_multiplier': target_multiplier,
            'target_amount': target_amount,
            'target_profit_amount': target_profit_amount,
            'credited_profit_amount': Decimal('0.00'),
            'daily_profit_amount': daily_profit_amount,
            'completion_days': completion_days,
            'accepted_days': 0,
            'status': AutomatedSignalPlanStatus.ACTIVE,
            'started_at': deposit.verified_at or timezone.now(),
        },
    )

    if created:
        return plan

    plan.user = deposit.user
    plan.principal_amount = principal_amount
    plan.target_multiplier = target_multiplier
    plan.target_amount = target_amount
    plan.target_profit_amount = target_profit_amount
    plan.credited_profit_amount = Decimal('0.00')
    plan.daily_profit_amount = daily_profit_amount
    plan.completion_days = completion_days
    plan.accepted_days = 0
    plan.status = AutomatedSignalPlanStatus.ACTIVE
    plan.started_at = deposit.verified_at or timezone.now()
    plan.completed_at = None
    plan.cancelled_at = None
    plan.save()

    plan.daily_signals.update(status=AutomatedUserSignalStatus.CANCELLED)
    return plan


def _apply_wallet_earning_delta(user, amount_delta):
    """
    Apply a signal profit delta to the user's wallet and earnings summary.
    """
    wallet, created = Wallet.objects.get_or_create(user=user)
    wallet.balance += amount_delta
    wallet.total_earnings += amount_delta

    if wallet.balance < 0:
        wallet.balance = Decimal('0.00')
    if wallet.total_earnings < 0:
        wallet.total_earnings = Decimal('0.00')

    wallet.save(update_fields=['balance', 'total_earnings', 'updated_at'])
    user.wallet_balance = wallet.balance
    user.total_earnings += amount_delta
    if user.total_earnings < 0:
        user.total_earnings = Decimal('0.00')
    user.save(update_fields=['wallet_balance', 'total_earnings', 'updated_at'])
    return wallet


def _reverse_signal_credit(signal):
    """
    Reverse the previously credited profit for an accepted automated signal.
    """
    if signal.status != AutomatedUserSignalStatus.ACCEPTED:
        return

    _apply_wallet_earning_delta(signal.user, -signal.profit_amount)

    if signal.transaction:
        signal.transaction.status = TransactionStatus.CANCELLED
        signal.transaction.completed_at = timezone.now()
        signal.transaction.description = f'Automated signal #{signal.id} profit reversed'
        signal.transaction.save(update_fields=['status', 'completed_at', 'description'])

    signal.status = AutomatedUserSignalStatus.CANCELLED
    signal.save(update_fields=['status', 'updated_at'])


def cancel_signal_plan_for_deposit(deposit):
    """
    Cancel a deposit's signal plan and reverse any credited automated profits.
    """
    plan = UserSignalPlan.objects.filter(deposit=deposit).first()
    if not plan:
        return

    accepted_signals = list(plan.daily_signals.select_related('transaction').filter(
        status=AutomatedUserSignalStatus.ACCEPTED,
    ))
    for signal in accepted_signals:
        _reverse_signal_credit(signal)

    plan.daily_signals.filter(status__in=[
        AutomatedUserSignalStatus.PENDING,
        AutomatedUserSignalStatus.EXPIRED,
    ]).update(status=AutomatedUserSignalStatus.CANCELLED)

    plan.credited_profit_amount = Decimal('0.00')
    plan.accepted_days = 0
    plan.status = AutomatedSignalPlanStatus.CANCELLED
    plan.cancelled_at = timezone.now()
    plan.completed_at = None
    plan.save(update_fields=[
        'credited_profit_amount',
        'accepted_days',
        'status',
        'cancelled_at',
        'completed_at',
        'updated_at',
    ])


def _expire_due_signals(user, now=None):
    """
    Expire any pending automated signals whose acceptance window has ended.
    """
    now = timezone.localtime(now or timezone.now())
    AutomatedUserSignal.objects.filter(
        user=user,
        status=AutomatedUserSignalStatus.PENDING,
        expires_at__lte=now,
    ).update(status=AutomatedUserSignalStatus.EXPIRED)


def ensure_user_daily_signals(user, now=None):
    """
    Generate today's pending signals for the user's active plans when the release window opens.
    """
    settings = get_signal_program_settings()
    now = timezone.localtime(now or timezone.now())
    _expire_due_signals(user, now)

    if not settings.is_enabled:
        return []

    active_plans = list(UserSignalPlan.objects.filter(
        user=user,
        status=AutomatedSignalPlanStatus.ACTIVE,
    ).select_related('deposit'))

    if not active_plans:
        return []

    current_wallet_balance = _get_user_wallet_balance(user)
    if current_wallet_balance <= Decimal('0.00') or not user.is_active_signal:
        return []

    release_context = _get_release_context(now, settings)
    signal_date = release_context['signal_date']
    slot_key = release_context['slot_key']
    scheduled_for = release_context['scheduled_for']
    expires_at = release_context['expires_at']

    if not _is_test_mode_enabled(settings):
        existing_today_pending = AutomatedUserSignal.objects.filter(
            user=user,
            signal_date=signal_date,
            status=AutomatedUserSignalStatus.PENDING,
            expires_at__gt=now,
        ).select_related('coin', 'plan').order_by('id')
        if existing_today_pending.exists():
            return list(existing_today_pending)

        existing_today_signals = AutomatedUserSignal.objects.filter(
            user=user,
            signal_date=signal_date,
        ).exclude(status=AutomatedUserSignalStatus.CANCELLED)
        if existing_today_signals.exists():
            return []

    if now < scheduled_for or now >= expires_at:
        return list(AutomatedUserSignal.objects.filter(
            user=user,
            slot_key=slot_key,
            status=AutomatedUserSignalStatus.PENDING,
            expires_at__gt=now,
        ).select_related('coin', 'plan').order_by('id'))

    signal_payload = _get_signal_payload(user, signal_date, settings)
    available_signals = []

    for plan in active_plans:
        _mark_plan_completed_if_needed(plan)
        if plan.status != AutomatedSignalPlanStatus.ACTIVE:
            continue

        first_release = _get_first_release_for_plan(plan, settings)
        if scheduled_for < first_release:
            continue

        signal, created = AutomatedUserSignal.objects.get_or_create(
            plan=plan,
            slot_key=slot_key,
            defaults={
                'user': user,
                'coin': signal_payload['coin'],
                'signal_type': signal_payload['signal_type'],
                'signal_date': signal_date,
                'slot_key': slot_key,
                'day_number': plan.accepted_days + 1,
                'scheduled_for': scheduled_for,
                'expires_at': expires_at,
                'status': AutomatedUserSignalStatus.PENDING,
                'profit_amount': _get_signal_profit_amount_for_plan(plan),
                'entry_price': signal_payload['entry_price'],
                'target_price': signal_payload['target_price'],
                'stop_loss': signal_payload['stop_loss'],
            },
        )

        if created and signal.profit_amount <= Decimal('0.00'):
            signal.status = AutomatedUserSignalStatus.CANCELLED
            signal.save(update_fields=['status', 'updated_at'])
            continue

        if not created and signal.status == AutomatedUserSignalStatus.PENDING:
            signal.user = user
            signal.coin = signal_payload['coin']
            signal.signal_type = signal_payload['signal_type']
            signal.signal_date = signal_date
            signal.slot_key = slot_key
            signal.day_number = plan.accepted_days + 1
            signal.scheduled_for = scheduled_for
            signal.expires_at = expires_at
            signal.profit_amount = _get_signal_profit_amount_for_plan(plan)
            signal.entry_price = signal_payload['entry_price']
            signal.target_price = signal_payload['target_price']
            signal.stop_loss = signal_payload['stop_loss']
            signal.save()

        if signal.status == AutomatedUserSignalStatus.PENDING and signal.expires_at > now:
            available_signals.append(signal)

    return available_signals


def dispatch_manual_signals_for_plans(plans, now=None):
    """
    Let admin manually send today's signal immediately for selected active plans.
    """
    settings = get_signal_program_settings()
    now = timezone.localtime(now or timezone.now())
    expiry_delta = _get_effective_expiry_delta(settings)
    release_context = _get_release_context(now, settings)
    signal_date = release_context['signal_date']
    slot_key = release_context['slot_key']
    success_count = 0
    skipped_count = 0

    for plan in plans.select_related('user', 'deposit'):
        if plan.status != AutomatedSignalPlanStatus.ACTIVE:
            skipped_count += 1
            continue

        if _get_user_wallet_balance(plan.user) <= Decimal('0.00') or not plan.user.is_active_signal:
            skipped_count += 1
            continue

        if not _is_test_mode_enabled(settings):
            already_has_signal_today = plan.daily_signals.filter(
                signal_date=signal_date,
            ).exclude(status=AutomatedUserSignalStatus.CANCELLED).exists()
            if already_has_signal_today:
                skipped_count += 1
                continue

        _mark_plan_completed_if_needed(plan)
        if plan.status != AutomatedSignalPlanStatus.ACTIVE:
            skipped_count += 1
            continue

        signal_payload = _get_signal_payload(plan.user, signal_date, settings)
        signal, created = AutomatedUserSignal.objects.get_or_create(
            plan=plan,
            slot_key=slot_key,
            defaults={
                'user': plan.user,
                'coin': signal_payload['coin'],
                'signal_type': signal_payload['signal_type'],
                'signal_date': signal_date,
                'slot_key': slot_key,
                'day_number': plan.accepted_days + 1,
                'scheduled_for': now,
                'expires_at': now + expiry_delta,
                'status': AutomatedUserSignalStatus.PENDING,
                'profit_amount': _get_signal_profit_amount_for_plan(plan),
                'entry_price': signal_payload['entry_price'],
                'target_price': signal_payload['target_price'],
                'stop_loss': signal_payload['stop_loss'],
            },
        )

        if signal.status == AutomatedUserSignalStatus.ACCEPTED:
            skipped_count += 1
            continue

        signal.user = plan.user
        signal.coin = signal_payload['coin']
        signal.signal_type = signal_payload['signal_type']
        signal.day_number = plan.accepted_days + 1
        signal.scheduled_for = now
        signal.expires_at = now + expiry_delta
        signal.status = AutomatedUserSignalStatus.PENDING
        signal.accepted_at = None
        signal.profit_amount = _get_signal_profit_amount_for_plan(plan)
        signal.entry_price = signal_payload['entry_price']
        signal.target_price = signal_payload['target_price']
        signal.stop_loss = signal_payload['stop_loss']
        signal.save()

        if signal.profit_amount <= Decimal('0.00'):
            signal.status = AutomatedUserSignalStatus.CANCELLED
            signal.save(update_fields=['status', 'updated_at'])
            skipped_count += 1
            continue

        success_count += 1

    return {
        'success_count': success_count,
        'skipped_count': skipped_count,
    }


def get_user_signal_dashboard_data(user, now=None):
    """
    Build dashboard-ready information for today's automated user signal.
    """
    settings = get_signal_program_settings()
    now = timezone.localtime(now or timezone.now())
    release_context = _get_release_context(now, settings)
    available_signals = ensure_user_daily_signals(user, now)
    active_plans = list(UserSignalPlan.objects.filter(
        user=user,
        status=AutomatedSignalPlanStatus.ACTIVE,
    ))
    current_wallet_balance = _get_user_wallet_balance(user)
    next_release_at = release_context['next_release_at']
    test_mode_enabled = _is_test_mode_enabled(settings)

    current_slot_signals = list(AutomatedUserSignal.objects.filter(
        user=user,
        slot_key=release_context['slot_key'],
    ).select_related('coin', 'plan').order_by('id'))
    today_signals = list(AutomatedUserSignal.objects.filter(
        user=user,
        signal_date=release_context['signal_date'],
    ).select_related('coin', 'plan').order_by('id'))

    if test_mode_enabled:
        status_scope_signals = current_slot_signals
    else:
        status_scope_signals = today_signals

    accepted_today = [signal for signal in status_scope_signals if signal.status == AutomatedUserSignalStatus.ACCEPTED]

    if status_scope_signals and not available_signals:
        if test_mode_enabled:
            next_release_at = release_context['next_release_at']
        else:
            next_release_at = _combine_local_datetime(
                release_context['signal_date'] + timedelta(days=1),
                settings.daily_signal_time,
            )

    active_investment_amount = sum(
        (plan.principal_amount for plan in active_plans),
        Decimal('0.00'),
    )
    first_plan_release = None

    if active_plans:
        first_plan_release = min(_get_first_release_for_plan(plan, settings) for plan in active_plans)
        if first_plan_release > now and not status_scope_signals:
            next_release_at = first_plan_release

    representative_signal = None
    state = 'inactive'
    profit_amount = Decimal('0.00')

    if not settings.is_enabled:
        state = 'disabled'
    elif available_signals:
        state = 'available'
        representative_signal = available_signals[0]
        profit_amount = sum((signal.profit_amount for signal in available_signals), Decimal('0.00'))
    elif accepted_today:
        state = 'accepted'
        representative_signal = accepted_today[0]
        profit_amount = sum((signal.profit_amount for signal in accepted_today), Decimal('0.00'))
    elif active_plans and current_wallet_balance <= Decimal('0.00'):
        state = 'no_balance'
    elif not active_plans:
        state = 'inactive'
    elif first_plan_release and first_plan_release > now and not status_scope_signals:
        state = 'scheduled'
    elif now >= release_context['expires_at'] and status_scope_signals:
        state = 'expired'
    elif now < release_context['scheduled_for']:
        state = 'scheduled'
    else:
        state = 'scheduled'

    return {
        'state': state,
        'settings': settings,
        'active_plan_count': len(active_plans),
        'active_investment_amount': _quantize_money(active_investment_amount),
        'wallet_signal_amount': _quantize_money(current_wallet_balance),
        'profit_amount': _quantize_money(profit_amount),
        'representative_signal': representative_signal,
        'available_signals': available_signals,
        'accepted_today': accepted_today,
        'today_signal_count': len(available_signals) or len(accepted_today),
        'next_release_at': next_release_at,
        'today_release_at': release_context['scheduled_for'],
        'today_expires_at': release_context['expires_at'],
        'current_expires_at': min((signal.expires_at for signal in available_signals), default=release_context['expires_at']),
        'target_multiplier': _get_plan_target_multiplier(settings),
        'completion_days': _get_plan_completion_days(settings),
        'has_future_signals': bool(active_plans),
        'test_mode_enabled': test_mode_enabled,
        'test_interval_minutes': settings.test_interval_minutes or DEFAULT_TEST_INTERVAL_MINUTES,
    }


def accept_available_daily_signals(user, now=None):
    """
    Accept all currently available automated signals for a user and credit profit.
    """
    with transaction.atomic():
        now = timezone.localtime(now or timezone.now())
        ensure_user_daily_signals(user, now)

        pending_signals = list(AutomatedUserSignal.objects.select_for_update().filter(
            user=user,
            status=AutomatedUserSignalStatus.PENDING,
            scheduled_for__lte=now,
            expires_at__gt=now,
        ).select_related('plan', 'coin'))

        if not pending_signals:
            return {
                'accepted_count': 0,
                'total_profit': Decimal('0.00'),
            }

        total_profit = sum((signal.profit_amount for signal in pending_signals), Decimal('0.00'))
        _apply_wallet_earning_delta(user, total_profit)
        wallet = Wallet.objects.select_for_update().get(user=user)

        accepted_count = 0
        for signal in pending_signals:
            reference_id = f'AUTO-SIGNAL-{signal.id}'
            transaction_record = Transaction.objects.create(
                wallet=wallet,
                transaction_type=TransactionType.SIGNAL_PROFIT,
                amount=signal.profit_amount,
                status=TransactionStatus.COMPLETED,
                reference_id=reference_id,
                description=(
                    f'Automated daily signal profit for deposit #{signal.plan.deposit_id} '
                    f'on day {signal.day_number}'
                ),
                completed_at=now,
            )

            signal.status = AutomatedUserSignalStatus.ACCEPTED
            signal.accepted_at = now
            signal.transaction = transaction_record
            signal.save(update_fields=['status', 'accepted_at', 'transaction', 'updated_at'])

            plan = signal.plan
            plan.accepted_days += 1
            plan.credited_profit_amount = _quantize_money(plan.credited_profit_amount + signal.profit_amount)
            plan.save(update_fields=['accepted_days', 'credited_profit_amount', 'updated_at'])
            _mark_plan_completed_if_needed(plan, completed_at=now)
            accepted_count += 1

        return {
            'accepted_count': accepted_count,
            'total_profit': _quantize_money(total_profit),
        }
