<?php
declare(strict_types=1);

namespace App\Services\Payout;

use App\Core\Database;
use App\Models\{CreatorBalance, CreatorLedger, WithdrawalRequest};
use PDO;

class ManualPayoutProvider implements PayoutProviderInterface
{
    public function __construct(private ?PDO $pdo = null)
    {
        $this->pdo ??= Database::connection();
    }

    public function createWithdrawalRequest(array $data): array
    {
        $this->pdo->beginTransaction();
        try {
            $request = (new WithdrawalRequest($this->pdo))->createRequest($data);
            (new CreatorBalance($this->pdo))->decreaseAvailable(
                (int) $data['user_id'],
                (float) $data['amount'],
                (int) $request['id'],
                'Para cekme talebi olusturuldu.',
                (string) ($data['currency'] ?? 'TRY')
            );
            $this->pdo->commit();

            return ['ok' => true, 'withdrawal' => $request];
        } catch (\Throwable $exception) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $exception;
        }
    }

    public function approve(int $withdrawalId, int $adminId, ?string $note = null): array
    {
        return $this->transition($withdrawalId, 'approved', $adminId, $note);
    }

    public function markProcessing(int $withdrawalId, int $adminId, ?string $note = null): array
    {
        return $this->transition($withdrawalId, 'processing', $adminId, $note);
    }

    public function markSent(int $withdrawalId, int $adminId, array $data = []): array
    {
        $this->pdo->beginTransaction();
        try {
            $model = new WithdrawalRequest($this->pdo);
            $current = $model->findDetailed($withdrawalId);
            if (!$current || !$model->canTransition((string) $current['status'], 'sent')) {
                throw new \RuntimeException('Bu talep gonderildi olarak isaretlenemez.');
            }

            $updated = $model->transition($withdrawalId, 'sent', [
                'admin_id' => $adminId,
                'admin_note' => $data['admin_note'] ?? null,
                'transaction_reference' => $data['transaction_reference'] ?? null,
                'receipt_file' => $data['receipt_file'] ?? null,
            ]);
            (new CreatorBalance($this->pdo))->markWithdrawn(
                (int) ($current['user_id'] ?? $current['creator_id']),
                (float) $current['amount'],
                $withdrawalId,
                'Manuel banka transferi gonderildi olarak isaretlendi.',
                (string) $current['currency']
            );
            $this->pdo->commit();

            return ['ok' => true, 'withdrawal' => $updated];
        } catch (\Throwable $exception) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $exception;
        }
    }

    public function reject(int $withdrawalId, int $adminId, string $reason): array
    {
        $this->pdo->beginTransaction();
        try {
            $model = new WithdrawalRequest($this->pdo);
            $current = $model->findDetailed($withdrawalId);
            if (!$current || !$model->canTransition((string) $current['status'], 'rejected')) {
                throw new \RuntimeException('Bu talep reddedilemez.');
            }

            $updated = $model->transition($withdrawalId, 'rejected', [
                'admin_id' => $adminId,
                'admin_note' => $reason,
            ]);
            (new CreatorBalance($this->pdo))->restoreAvailable(
                (int) ($current['user_id'] ?? $current['creator_id']),
                (float) $current['amount'],
                $withdrawalId,
                'withdrawal_rejected',
                'Para cekme talebi reddedildi: ' . $reason,
                true,
                (string) $current['currency']
            );
            $this->pdo->commit();

            return ['ok' => true, 'withdrawal' => $updated];
        } catch (\Throwable $exception) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $exception;
        }
    }

    public function cancel(int $withdrawalId, int $userId): array
    {
        $this->pdo->beginTransaction();
        try {
            $model = new WithdrawalRequest($this->pdo);
            $current = $model->findDetailed($withdrawalId);
            $ownerId = (int) ($current['user_id'] ?? $current['creator_id'] ?? 0);
            if (!$current || $ownerId !== $userId || !$model->canTransition((string) $current['status'], 'cancelled')) {
                throw new \RuntimeException('Bu talep iptal edilemez.');
            }

            $updated = $model->transition($withdrawalId, 'cancelled');
            (new CreatorBalance($this->pdo))->restoreAvailable(
                $userId,
                (float) $current['amount'],
                $withdrawalId,
                'manual_adjustment',
                'Para cekme talebi kullanici tarafindan iptal edildi.',
                false,
                (string) $current['currency']
            );
            $this->pdo->commit();

            return ['ok' => true, 'withdrawal' => $updated];
        } catch (\Throwable $exception) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $exception;
        }
    }

    private function transition(int $withdrawalId, string $toStatus, int $adminId, ?string $note = null): array
    {
        $this->pdo->beginTransaction();
        try {
            $model = new WithdrawalRequest($this->pdo);
            $current = $model->findDetailed($withdrawalId);
            if (!$current || !$model->canTransition((string) $current['status'], $toStatus)) {
                throw new \RuntimeException('Gecersiz para cekme durum gecisi.');
            }

            $updated = $model->transition($withdrawalId, $toStatus, [
                'admin_id' => $adminId,
                'admin_note' => $note,
            ]);

            if ($toStatus === 'approved') {
                $amount = (float) $current['amount'];
                (new CreatorLedger($this->pdo))->createEntry([
                    'user_id' => (int) ($current['user_id'] ?? $current['creator_id']),
                    'withdrawal_id' => $withdrawalId,
                    'type' => 'withdrawal_approved',
                    'amount' => 0,
                    'balance_before' => $amount,
                    'balance_after' => $amount,
                    'currency' => (string) $current['currency'],
                    'description' => 'Para cekme talebi admin tarafindan onaylandi.',
                ]);
            }

            $this->pdo->commit();

            return ['ok' => true, 'withdrawal' => $updated];
        } catch (\Throwable $exception) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $exception;
        }
    }
}
