<?php

namespace Services;

use Libs\BashExecutor;

class TokenCreateService
{
    private \PDO $pdo;
    private string $easyCliPath;

    public function __construct()
    {
        if (!isset($GLOBALS['pdo']) || !$GLOBALS['pdo'] instanceof \PDO) {
            throw new \RuntimeException('Global PDO connection not initialized');
        }

        $this->pdo = $GLOBALS['pdo'];
        $this->easyCliPath = dirname(__DIR__) . '/bin/easycli';
    }

    public function createToken(
        int $tokenCommoditiesAmount,
        int $tokenCommoditiesValue,
        string $tokenUf,
        string $tokenCity,
        string $tokenContent,
        string $tokenFlag,
        int $walletId,
        int $chainId,
        int $commoditiesId,
        int $cprId,
        int $userId
    ): array {
        $mintData = $this->mintToken($tokenContent);
        $tokenExternalId = $mintData['tokenId'];
        $txHash = $mintData['tx'];

        $tokenId = $this->insertToken([
            'token_external_id' => $tokenExternalId,
            'token_commodities_amount' => $tokenCommoditiesAmount,
            'token_commodities_value' => $tokenCommoditiesValue,
            'token_uf' => $tokenUf,
            'token_city' => $tokenCity,
            'token_content' => $tokenContent,
            'token_flag' => $tokenFlag,
            'wallet_id' => $walletId,
            'chain_id' => $chainId,
            'commodities_id' => $commoditiesId,
            'cpr_id' => $cprId,
            'user_id' => $userId,
        ]);

        return [
            'token_id' => $tokenId,
            'token_external_id' => $tokenExternalId,
            'tx_hash' => $txHash,
            'raw_output' => $mintData['raw_output'],
        ];
    }

    private function mintToken(string $content): array
    {
        if (!is_file($this->easyCliPath) || !is_executable($this->easyCliPath)) {
            throw new \RuntimeException('easycli executable not found or not executable');
        }

        $command = sprintf('%s token mint %s', escapeshellarg($this->easyCliPath), escapeshellarg($content));
        $result = BashExecutor::run($command, 120);

        if (($result['exitCode'] ?? 1) !== 0) {
            $message = $result['error'] ?: $result['output'] ?: 'Unknown easycli error';
            throw new \RuntimeException('easycli mint failed: ' . $message);
        }

        $combinedOutput = trim(implode("\n", array_filter([
            trim($result['output'] ?? ''),
            trim($result['error'] ?? ''),
        ])));

        $tokenId = $this->extractValue('tokenId', $combinedOutput);
        $txHash = $this->extractValue('tx', $combinedOutput);

        if ($tokenId === null) {
            throw new \RuntimeException('Unable to parse tokenId from easycli output: ' . $combinedOutput);
        }

        return [
            'tokenId' => $tokenId,
            'tx' => $txHash,
            'raw_output' => $combinedOutput,
        ];
    }

    private function extractValue(string $key, string $content): ?string
    {
        $pattern = sprintf('/%s\s*=\s*([^\s]+)/i', preg_quote($key, '/'));
        if (preg_match($pattern, $content, $matches)) {
            return trim($matches[1]);
        }

        return null;
    }

    private function insertToken(array $data): int
    {
        $stmt = $this->pdo->prepare(
            'INSERT INTO "token" (
                token_external_id,
                token_commodities_amount,
                token_commodities_value,
                token_uf,
                token_city,
                token_content,
                token_flag,
                wallet_id,
                chain_id,
                commodities_id,
                cpr_id,
                user_id
            ) VALUES (
                :token_external_id,
                :token_commodities_amount,
                :token_commodities_value,
                :token_uf,
                :token_city,
                :token_content,
                :token_flag,
                :wallet_id,
                :chain_id,
                :commodities_id,
                :cpr_id,
                :user_id
            ) RETURNING token_id'
        );

        $stmt->execute($data);

        return (int) $stmt->fetchColumn();
    }
}
