<?php

namespace Core\App\Cron\Tasks;

use Core\App\Cron\CronTaskInterface;
use Core\App\Models\Settings;
use Core\App\Models\SecurityLog;

class GoogleSheetsExportTask implements CronTaskInterface
{
    private Settings $settings;

    public function __construct()
    {
        $this->settings = new Settings();
    }

    public function name(): string
    {
        return 'google_sheets_export';
    }

    public function run(): array
    {
        $oauthEnabled = (string)$this->settings->get('gsheets_oauth_enabled', '0');
        $legacyEnabled = (int)$this->settings->get('gsheets_enabled', 0);

        // تحقق من وجود أي نظام مُفعّل
        if ($oauthEnabled !== '1' && $legacyEnabled !== 1) {
            return ['success' => true, 'skipped' => true, 'reason' => 'disabled'];
        }

        // إعدادات مشتركة
        $maxPerRun   = max(1, (int)$this->settings->get('gsheets_max_per_run', 25));
        $maxAttempts = max(1, (int)$this->settings->get('gsheets_max_attempts', 12));

        // OAuth mode
        if ($oauthEnabled === '1') {
            $spreadsheetId = trim((string)$this->settings->get('gsheets_oauth_spreadsheet_id', ''));
            $sheetName     = trim((string)$this->settings->get('gsheets_oauth_sheet_name', 'Orders'));
            if ($spreadsheetId === '') {
                return ['success' => false, 'error' => 'oauth_not_configured'];
            }
            $useOAuth = true;
            $oauthCtrl = new \Core\App\Controllers\GoogleSheetsOAuthController();
        } else {
            // Legacy Service Account
            $spreadsheetId = trim((string)$this->settings->get('gsheets_spreadsheet_id', ''));
            $sheetName     = trim((string)$this->settings->get('gsheets_sheet_name', 'Orders'));
            $credPath      = trim((string)$this->settings->get('gsheets_credentials_path', 'storage/credentials/google-service-account.json'));

            if ($spreadsheetId === '' || $sheetName === '' || $credPath === '') {
                return ['success' => false, 'error' => 'missing_configuration'];
            }
            $credFull = $this->resolveCredentialsPath($credPath);
            if ($credFull === null || !file_exists($credFull)) {
                return ['success' => false, 'error' => 'credentials_not_found'];
            }
            $useOAuth = false;
        }

        $pendingDir = ROOT_PATH . '/storage/queue/gsheets/pending';
        if (!is_dir($pendingDir)) {
            return ['success' => true, 'processed' => 0, 'sent' => 0, 'failed' => 0];
        }

        $files = glob($pendingDir . '/*.json') ?: [];
        sort($files);

        $processed = 0;
        $sent = 0;
        $failed = 0;

        foreach ($files as $file) {
            if ($processed >= $maxPerRun) {
                break;
            }

            $processed++;

            $job = json_decode((string)@file_get_contents($file), true);
            if (!is_array($job) || empty($job['row'])) {
                $this->moveToDead($file, 'invalid_job');
                $failed++;
                continue;
            }

            $attempts = (int)($job['attempts'] ?? 0);
            $orderId  = (int)($job['order_id'] ?? 0);

            try {
                if ($useOAuth ?? false) {
                    $oauthCtrl->appendRow($this->toSheetRowValues($job['row']));
                } else {
                    $token = $this->getAccessToken($credFull);
                    $this->appendRow(
                        $token,
                        $spreadsheetId,
                        $sheetName,
                        $this->toSheetRowValues($job['row'])
                    );
                }

                $this->moveToSent($file);
                $sent++;
            } catch (\Throwable $e) {
                $attempts++;
                $job['attempts'] = $attempts;
                $job['last_error'] = $e->getMessage();
                $job['last_attempt_at'] = date('Y-m-d H:i:s');

                @file_put_contents($file, json_encode($job, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

                SecurityLog::log(
                    'gsheets_export',
                    'Order #' . $orderId . ' export failed (attempt ' . $attempts . '): ' . $e->getMessage()
                );

                if ($attempts >= $maxAttempts) {
                    $this->moveToDead($file, 'max_attempts');
                }

                $failed++;
            }
        }

        return [
            'success'   => true,
            'processed' => $processed,
            'sent'      => $sent,
            'failed'    => $failed,
            'max_per_run' => $maxPerRun,
        ];
    }

    private function resolveCredentialsPath(string $path): ?string
    {
        // Disallow traversal
        if (strpos($path, '..') !== false) {
            return null;
        }

        // Disallow putting it in public
        $normalized = ltrim(str_replace('\\', '/', $path), '/');
        if (strpos($normalized, 'public/') === 0) {
            return null;
        }

        return ROOT_PATH . '/' . $normalized;
    }

    private function moveToSent(string $file): void
    {
        $dir = ROOT_PATH . '/storage/queue/gsheets/sent/' . date('Ym');
        if (!is_dir($dir)) {
            @mkdir($dir, 0777, true);
        }
        @rename($file, $dir . '/' . basename($file));
    }

    private function moveToDead(string $file, string $reason): void
    {
        $dir = ROOT_PATH . '/storage/queue/gsheets/dead/' . date('Ym');
        if (!is_dir($dir)) {
            @mkdir($dir, 0777, true);
        }

        $target = $dir . '/' . basename($file);
        @rename($file, $target);

        SecurityLog::log('gsheets_export', 'Moved job to dead-letter (' . $reason . '): ' . basename($file));
    }

    /**
     * Convert associative row payload to ordered sheet columns.
     */
    private function toSheetRowValues(array $row): array
    {
        return [
            (string)($row['order_id'] ?? ''),
            (string)($row['date'] ?? ''),
            (string)($row['product_title'] ?? ''),
            (string)($row['quantity'] ?? ''),
            (string)($row['variant_size'] ?? ''),
            (string)($row['variant_color'] ?? ''),
            (string)($row['full_name'] ?? ''),
            (string)($row['phone'] ?? ''),
            (string)($row['wilaya'] ?? ''),
            (string)($row['commune'] ?? ''),
            (string)($row['shipping_type'] ?? ''),
            (string)($row['shipping_price'] ?? ''),
            (string)($row['total'] ?? ''),
            (string)($row['status'] ?? ''),
            (string)($row['notes'] ?? ''),
            (string)($row['source'] ?? ''),
            (string)($row['whatsapp_click'] ?? ''),
            (string)($row['shipping_company'] ?? ''),
        ];
    }

    /**
     * OAuth2 Service Account: create access token for Sheets.
     */
    private function getAccessToken(string $credentialsFile): string
    {
        $json = json_decode((string)file_get_contents($credentialsFile), true);
        if (!is_array($json)) {
            throw new \RuntimeException('invalid_credentials_json');
        }

        $clientEmail = (string)($json['client_email'] ?? '');
        $privateKey  = (string)($json['private_key'] ?? '');
        $tokenUri    = (string)($json['token_uri'] ?? 'https://oauth2.googleapis.com/token');

        if ($clientEmail === '' || $privateKey === '') {
            throw new \RuntimeException('credentials_missing_fields');
        }

        $now = time();
        $header  = $this->base64UrlEncode(json_encode(['alg' => 'RS256', 'typ' => 'JWT']));
        $payload = $this->base64UrlEncode(json_encode([
            'iss'   => $clientEmail,
            'scope' => 'https://www.googleapis.com/auth/spreadsheets',
            'aud'   => $tokenUri,
            'iat'   => $now,
            'exp'   => $now + 3600,
        ]));

        $unsigned = $header . '.' . $payload;

        $signature = '';
        $ok = openssl_sign($unsigned, $signature, $privateKey, 'sha256');
        if (!$ok) {
            throw new \RuntimeException('jwt_sign_failed');
        }

        $jwt = $unsigned . '.' . $this->base64UrlEncode($signature);

        $postFields = http_build_query([
            'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
            'assertion'  => $jwt,
        ]);

        $ch = curl_init($tokenUri);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $postFields,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/x-www-form-urlencoded'],
            CURLOPT_CONNECTTIMEOUT => 4,
            CURLOPT_TIMEOUT        => 8,
        ]);

        $res = curl_exec($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err  = curl_error($ch);
        curl_close($ch);

        if ($res === false) {
            throw new \RuntimeException('token_request_failed: ' . $err);
        }

        $data = json_decode($res, true);
        if ($code < 200 || $code >= 300 || !is_array($data) || empty($data['access_token'])) {
            throw new \RuntimeException('token_invalid_response');
        }

        return (string)$data['access_token'];
    }

    private function appendRow(string $accessToken, string $spreadsheetId, string $sheetName, array $values): void
    {
        $range = rawurlencode($sheetName . '!A1');
        $url = 'https://sheets.googleapis.com/v4/spreadsheets/'
            . rawurlencode($spreadsheetId)
            . '/values/' . $range
            . ':append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS';

        $body = json_encode([
            'values' => [$values]
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $body,
            CURLOPT_HTTPHEADER     => [
                'Authorization: Bearer ' . $accessToken,
                'Content-Type: application/json'
            ],
            CURLOPT_CONNECTTIMEOUT => 4,
            CURLOPT_TIMEOUT        => 10,
        ]);

        $res = curl_exec($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err = curl_error($ch);
        curl_close($ch);

        if ($res === false) {
            throw new \RuntimeException('sheets_request_failed: ' . $err);
        }

        if ($code < 200 || $code >= 300) {
            throw new \RuntimeException('sheets_http_' . $code);
        }
    }

    private function base64UrlEncode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
}