<?php
declare(strict_types=1);

namespace App\Models;

class CreatorBalance extends BaseModel
{
    protected string $table = 'creator_balances';

    public function findOrCreate(int $userId, string $currency = 'TRY'): array
    {
        $currency = strtoupper(substr($currency, 0, 3)) ?: 'TRY';
        $statement = $this->db()->prepare(
            'SELECT * FROM creator_balances
             WHERE user_id = :user_id OR creator_id = :creator_id
             ORDER BY id ASC
             LIMIT 1'
        );
        $statement->execute(['user_id' => $userId, 'creator_id' => $userId]);
        $balance = $statement->fetch();

        if ($balance) {
            if (empty($balance['user_id'])) {
                $this->db()->prepare('UPDATE creator_balances SET user_id = :user_id WHERE id = :id')
                    ->execute(['user_id' => $userId, 'id' => (int) $balance['id']]);
                $balance['user_id'] = $userId;
            }

            return $balance;
        }

        $insert = $this->db()->prepare(
            'INSERT INTO creator_balances
                (creator_id, user_id, pending_balance, available_balance, withdrawn_balance, rejected_balance,
                 currency, created_at, updated_at)
             VALUES
                (:creator_id, :user_id, 0, 0, 0, 0, :currency, NOW(), NOW())'
        );
        $insert->execute([
            'creator_id' => $userId,
            'user_id' => $userId,
            'currency' => $currency,
        ]);

        return $this->find((int) $this->db()->lastInsertId()) ?? [
            'creator_id' => $userId,
            'user_id' => $userId,
            'pending_balance' => 0,
            'available_balance' => 0,
            'withdrawn_balance' => 0,
            'rejected_balance' => 0,
            'currency' => $currency,
        ];
    }

    public function recordSale(array $order, int $balanceHoldDays = 0): array
    {
        $userId = (int) ($order['creator_id'] ?? $order['user_id'] ?? 0);
        $orderId = (int) ($order['id'] ?? 0);
        $currency = (string) ($order['currency'] ?? 'TRY');
        $netAmount = round(max(0, (float) ($order['creator_net_amount'] ?? $order['creator_amount'] ?? 0)), 2);

        if ($userId <= 0 || $orderId <= 0 || $netAmount <= 0) {
            return [];
        }

        $ledger = new CreatorLedger($this->db());
        if ($ledger->existsForOrderType($userId, $orderId, 'sale_income')) {
            return $this->findOrCreate($userId, $currency);
        }

        $balance = $this->findOrCreate($userId, $currency);
        $targetColumn = $balanceHoldDays > 0 ? 'pending_balance' : 'available_balance';
        $before = round((float) ($balance[$targetColumn] ?? 0), 2);
        $after = round($before + $netAmount, 2);
        $availableAt = $balanceHoldDays > 0
            ? date('Y-m-d H:i:s', time() + ($balanceHoldDays * 86400))
            : date('Y-m-d H:i:s');

        $update = $this->db()->prepare(
            "UPDATE creator_balances
             SET {$targetColumn} = {$targetColumn} + :amount,
                 currency = :currency,
                 updated_at = NOW()
             WHERE id = :id"
        );
        $update->execute([
            'amount' => $netAmount,
            'currency' => strtoupper(substr($currency, 0, 3)) ?: 'TRY',
            'id' => (int) $balance['id'],
        ]);

        $description = 'Siparis kazanci: ' . (string) ($order['order_no'] ?? ('#' . $orderId));
        $ledger->createEntry([
            'user_id' => $userId,
            'order_id' => $orderId,
            'type' => 'sale_income',
            'amount' => $netAmount,
            'balance_before' => $before,
            'balance_after' => $after,
            'currency' => $currency,
            'description' => $description,
            'available_at' => $availableAt,
        ]);

        $this->recordSaleDeduction($ledger, $userId, $orderId, 'provider_fee_deducted', (float) ($order['provider_fee_amount'] ?? 0), $after, $currency, 'Provider ucreti kesintisi');
        $this->recordSaleDeduction($ledger, $userId, $orderId, 'commission_deducted', (float) ($order['commission_amount'] ?? $order['platform_fee'] ?? 0), $after, $currency, 'Site komisyonu kesintisi');
        $this->recordSaleDeduction($ledger, $userId, $orderId, 'tax_deducted', (float) ($order['tax_amount'] ?? 0), $after, $currency, 'Vergi kesintisi');

        return $this->findOrCreate($userId, $currency);
    }

    public function decreaseAvailable(int $userId, float $amount, int $withdrawalId, string $description, string $currency = 'TRY'): array
    {
        $amount = round(max(0, $amount), 2);
        $balance = $this->findOrCreate($userId, $currency);
        $before = round((float) ($balance['available_balance'] ?? 0), 2);

        if ($amount <= 0 || $before < $amount) {
            throw new \RuntimeException('Cekilebilir bakiye yetersiz.');
        }

        $after = round($before - $amount, 2);
        $this->db()->prepare(
            'UPDATE creator_balances
             SET available_balance = available_balance - :amount,
                 updated_at = NOW()
             WHERE id = :id'
        )->execute(['amount' => $amount, 'id' => (int) $balance['id']]);

        (new CreatorLedger($this->db()))->createEntry([
            'user_id' => $userId,
            'withdrawal_id' => $withdrawalId,
            'type' => 'withdrawal_requested',
            'amount' => -$amount,
            'balance_before' => $before,
            'balance_after' => $after,
            'currency' => $currency,
            'description' => $description,
        ]);

        return $this->findOrCreate($userId, $currency);
    }

    public function restoreAvailable(int $userId, float $amount, int $withdrawalId, string $type, string $description, bool $countRejected = false, string $currency = 'TRY'): array
    {
        $amount = round(max(0, $amount), 2);
        $balance = $this->findOrCreate($userId, $currency);
        $before = round((float) ($balance['available_balance'] ?? 0), 2);
        $after = round($before + $amount, 2);

        $sql = 'UPDATE creator_balances
                SET available_balance = available_balance + :amount';
        if ($countRejected) {
            $sql .= ', rejected_balance = rejected_balance + :rejected_amount';
        }
        $sql .= ', updated_at = NOW() WHERE id = :id';

        $params = ['amount' => $amount, 'id' => (int) $balance['id']];
        if ($countRejected) {
            $params['rejected_amount'] = $amount;
        }
        $this->db()->prepare($sql)->execute($params);

        (new CreatorLedger($this->db()))->createEntry([
            'user_id' => $userId,
            'withdrawal_id' => $withdrawalId,
            'type' => $type,
            'amount' => $amount,
            'balance_before' => $before,
            'balance_after' => $after,
            'currency' => $currency,
            'description' => $description,
        ]);

        return $this->findOrCreate($userId, $currency);
    }

    public function markWithdrawn(int $userId, float $amount, int $withdrawalId, string $description, string $currency = 'TRY'): array
    {
        $amount = round(max(0, $amount), 2);
        $balance = $this->findOrCreate($userId, $currency);
        $before = round((float) ($balance['withdrawn_balance'] ?? 0), 2);
        $after = round($before + $amount, 2);

        $this->db()->prepare(
            'UPDATE creator_balances
             SET withdrawn_balance = withdrawn_balance + :amount,
                 updated_at = NOW()
             WHERE id = :id'
        )->execute(['amount' => $amount, 'id' => (int) $balance['id']]);

        (new CreatorLedger($this->db()))->createEntry([
            'user_id' => $userId,
            'withdrawal_id' => $withdrawalId,
            'type' => 'withdrawal_sent',
            'amount' => $amount,
            'balance_before' => $before,
            'balance_after' => $after,
            'currency' => $currency,
            'description' => $description,
        ]);

        return $this->findOrCreate($userId, $currency);
    }

    private function recordSaleDeduction(CreatorLedger $ledger, int $userId, int $orderId, string $type, float $amount, float $balance, string $currency, string $description): void
    {
        $amount = round(max(0, $amount), 2);
        if ($amount <= 0) {
            return;
        }

        $ledger->createEntry([
            'user_id' => $userId,
            'order_id' => $orderId,
            'type' => $type,
            'amount' => -$amount,
            'balance_before' => $balance,
            'balance_after' => $balance,
            'currency' => $currency,
            'description' => $description,
        ]);
    }
}
