<?php

namespace App\Services\Import\Handlers;

use App\Models\Account;
use App\Models\Booking;
use App\Models\Import;
use App\Models\Payment;
use App\Models\PaymentSetting;
use App\Services\Import\DateNormalizer;
use App\Services\Import\BaseEntityHandler;
use Illuminate\Support\Facades\Log;
class PaymentHandler extends BaseEntityHandler
{
    public function getEntityClass(): string
    {
        return Payment::class;
    }

    /**
     * Override validate to skip validation if amount is empty.
     */
    public function validate(array $mapped): array
    {
        $amount = $mapped['amount'] ?? null;
        if (empty($amount) || !is_numeric($amount)) {
            return ['valid' => true, 'errors' => []];
        }

        return parent::validate($mapped);
    }

    public function resolve(array $mapped, array $context = []): mixed
    {
        $accountId = $mapped['account_id'] ?? $context['account']?->entity?->id ?? null;
        $reference = $mapped['reference'] ?? null;

        if (! $accountId || ! $reference) {
            return null;
        }

        return Payment::where('account_id', $accountId)
            ->where('reference', $reference)
            ->first();
    }

    public function process(Import $import, array $mapped, array $raw, array $context = []): array
    {
        // Skip if amount is empty or invalid
        $amount = $mapped['amount'] ?? null;
        if (empty($amount) || !is_numeric($amount)) {
            return [
                'action' => 'skipped',
                'message' => 'Payment amount is empty or invalid',
            ];
        }

        // Resolve account - either from mapped data or context
        $accountId = $mapped['account_id'] ?? $context['account']?->entity?->id ?? null;

        if (! $accountId) {
            return [
                'action' => 'skipped',
                'message' => 'Payment requires an account',
            ];
        }

        // Check for duplicates if configured
        if ($this->getOption('deduplicate_by', [])) {
            $existing = $this->resolve($mapped, ['account' => (object) ['entity' => (object) ['id' => $accountId]]]);
            if ($existing) {
                return [
                    'action' => 'skipped',
                    'entity' => $existing,
                    'message' => 'Payment already exists (duplicate by reference)',
                ];
            }
        }

        // Build payment payload
        $payload = $this->buildPayload($mapped, new Payment);
        $payload['account_id'] = $accountId;
        $payload['created_by'] = $context['user']?->getAuthIdentifier();

        // Get account balance before payment
        $account = Account::find($accountId);
        $balanceBefore = $account ? (float) ($account->balance_amount ?? 0) : 0;

        // Create payment
        $payment = new Payment;
        $payment->fill($payload);
        $payment->balance_before = $balanceBefore;

        try {
            $payment->save();
        } catch (\Throwable $e) {
            // Handle unique constraint violations gracefully
            if (str_contains($e->getMessage(), 'payments_account_id_reference_unique')) {
                return [
                    'action' => 'skipped',
                    'message' => 'Payment duplicate detected (database constraint)',
                ];
            }
            throw $e;
        }

        // Create booking if configured
        if ($this->getOption('create_booking', true) && isset($payment->amount)) {
            try {
                Booking::create([
                    'account_id' => $accountId,
                    'payment_id' => $payment->id,
                    'amount_cents' => (int) round(((float) $payment->amount) * 100),
                    'type' => 'credit',
                    'description' => $payment->reference ? ('Plačilo '.$payment->reference) : 'Plačilo',
                    'booked_at' => $payment->paid_at ?? now(),
                ]);
            } catch (\Throwable $e) {
                Log::warning('Failed to create booking for payment', [
                    'payment_id' => $payment->id,
                    'error' => $e->getMessage(),
                ]);
            }
        }

        // Create activity if configured
        if ($this->getOption('create_activity', false)) {
            $this->createPaymentActivity($payment, $account, $balanceBefore);
        }

        return [
            'action' => 'inserted',
            'entity' => $payment,
            'applied_fields' => array_keys($payload),
        ];
    }

    protected function buildPayload(array $mapped, $model): array
    {
        $payload = [];

        // Map payment fields
        if (isset($mapped['reference'])) {
            $payload['reference'] = is_string($mapped['reference']) ? trim($mapped['reference']) : $mapped['reference'];
        }

        // Handle amount - support both amount and amount_cents
        if (array_key_exists('amount', $mapped)) {
            $payload['amount'] = is_string($mapped['amount']) ? (float) str_replace(',', '.', $mapped['amount']) : (float) $mapped['amount'];
        } elseif (array_key_exists('amount_cents', $mapped)) {
            $payload['amount'] = ((int) $mapped['amount_cents']) / 100.0;
        }

        // Payment date - support both paid_at and payment_date
        $dateValue = $mapped['paid_at'] ?? $mapped['payment_date'] ?? null;
        if ($dateValue) {
            $payload['paid_at'] = DateNormalizer::toDate((string) $dateValue);
        }

        $payload['currency'] = $mapped['currency'] ?? 'EUR';

        // Handle meta
        $meta = [];
        if (is_array($mapped['meta'] ?? null)) {
            $meta = $mapped['meta'];
        }
        if (! empty($mapped['payment_nu'])) {
            $meta['payment_nu'] = trim((string) $mapped['payment_nu']);
        }
        if (! empty($meta)) {
            $payload['meta'] = $meta;
        }

        return $payload;
    }

    protected function createPaymentActivity(Payment $payment, ?Account $account, float $balanceBefore): void
    {
        try {
            $settings = PaymentSetting::first();
            if (! $settings || ! ($settings->create_activity_on_payment ?? false)) {
                return;
            }

            $amountCents = (int) round(((float) $payment->amount) * 100);
            $note = $settings->activity_note_template ?? 'Prejeto plačilo';
            $note = str_replace(
                ['{amount}', '{currency}'],
                [number_format($amountCents / 100, 2, ',', '.'), $payment->currency ?? 'EUR'],
                $note
            );

            // Get updated balance
            $account?->refresh();
            $balanceAfter = $account ? (float) ($account->balance_amount ?? 0) : 0;

            $beforeStr = number_format($balanceBefore, 2, ',', '.').' '.($payment->currency ?? 'EUR');
            $afterStr = number_format($balanceAfter, 2, ',', '.').' '.($payment->currency ?? 'EUR');
            $note .= " (Stanje pred: {$beforeStr}, Stanje po: {$afterStr}; Izvor: plačilo)";

            // Resolve client_case_id
            $account?->loadMissing('contract');
            $clientCaseId = $account?->contract?->client_case_id;

            if ($clientCaseId) {
                $activity = \App\Models\Activity::create([
                    'due_date' => null,
                    'amount' => $amountCents / 100,
                    'note' => $note,
                    'action_id' => $settings->default_action_id,
                    'decision_id' => $settings->default_decision_id,
                    'client_case_id' => $clientCaseId,
                    'contract_id' => $account->contract_id,
                ]);
                $payment->update(['activity_id' => $activity->id]);
            }
        } catch (\Throwable $e) {
            Log::warning('Failed to create activity for payment', [
                'payment_id' => $payment->id,
                'error' => $e->getMessage(),
            ]);
        }
    }
}
