<?php
namespace Modules\GoogleIntegration\Controllers;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Security\Crypto;
use Gestoreasy\Core\Config\Config;
use Gestoreasy\Core\Audit\AuditLogger;

class GoogleCalendarController {
    private function pdo(): \PDO { return ConnectionManager::forTenant(); }
    private function ensureTables(\PDO $pdo): void {
        try {
            $pdo->exec('CREATE TABLE IF NOT EXISTS google_calendar_settings (
              id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
              calendar_id VARCHAR(128) NULL,
              client_id_enc BLOB NULL,
              client_id_iv BLOB NULL,
              client_id_tag BLOB NULL,
              client_id_salt BLOB NULL,
              client_secret_enc BLOB NULL,
              client_secret_iv BLOB NULL,
              client_secret_tag BLOB NULL,
              client_secret_salt BLOB NULL,
              refresh_token_enc BLOB NULL,
              refresh_token_iv BLOB NULL,
              refresh_token_tag BLOB NULL,
              refresh_token_salt BLOB NULL,
              created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
              updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
        } catch (\Throwable $e) {}
        try {
            $pdo->exec('CREATE TABLE IF NOT EXISTS google_calendar_logs (
              id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
              operation VARCHAR(64) NOT NULL,
              status INT NULL,
              request_payload MEDIUMTEXT NULL,
              response_body MEDIUMTEXT NULL,
              error_message TEXT NULL,
              created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
        } catch (\Throwable $e) {}
    }

    private function loadConfig(\PDO $pdo): array {
        $row = $pdo->query('SELECT * FROM google_calendar_settings ORDER BY id DESC LIMIT 1')->fetch();
        if (!$row) return [];
        $calendarId = $row['calendar_id'] ?? null;
        $clientId = null; $clientSecret = null; $refreshToken = null;
        try {
            if (!empty($row['client_id_enc'])) { $d = Crypto::decryptJson($row['client_id_enc'], $row['client_id_iv'], $row['client_id_tag'], $row['client_id_salt']); $clientId = (string)($d['client_id'] ?? null); }
            if (!empty($row['client_secret_enc'])) { $d = Crypto::decryptJson($row['client_secret_enc'], $row['client_secret_iv'], $row['client_secret_tag'], $row['client_secret_salt']); $clientSecret = (string)($d['client_secret'] ?? null); }
            if (!empty($row['refresh_token_enc'])) { $d = Crypto::decryptJson($row['refresh_token_enc'], $row['refresh_token_iv'], $row['refresh_token_tag'], $row['refresh_token_salt']); $refreshToken = (string)($d['refresh_token'] ?? null); }
        } catch (\Throwable $e) {}
        return [ 'calendar_id' => $calendarId, 'client_id' => $clientId, 'client_secret' => $clientSecret, 'refresh_token' => $refreshToken ];
    }

    public function getConfig(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $cfg = $this->loadConfig($pdo);
        return Response::json(['config' => [
            'calendar_id' => $cfg['calendar_id'] ?? null,
            'has_client' => !empty($cfg['client_id']) && !empty($cfg['client_secret']),
            'has_refresh_token' => !empty($cfg['refresh_token'])
        ]]);
    }

    public function saveConfig(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $calendarId = (string)($r->body['calendar_id'] ?? 'primary');
        $clientId = (string)($r->body['client_id'] ?? '');
        $clientSecret = (string)($r->body['client_secret'] ?? '');
        $refreshToken = (string)($r->body['refresh_token'] ?? '');
        if ($clientId === '' || $clientSecret === '' || $refreshToken === '') {
            return Response::json(['error' => 'client_id, client_secret e refresh_token requeridos'], 422);
        }
        $encClient = Crypto::encryptJson(['client_id' => $clientId]);
        $encSecret = Crypto::encryptJson(['client_secret' => $clientSecret]);
        $encRefresh = Crypto::encryptJson(['refresh_token' => $refreshToken]);
        $stmt = $pdo->prepare('INSERT INTO google_calendar_settings (calendar_id, client_id_enc, client_id_iv, client_id_tag, client_id_salt, client_secret_enc, client_secret_iv, client_secret_tag, client_secret_salt, refresh_token_enc, refresh_token_iv, refresh_token_tag, refresh_token_salt) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
        $stmt->execute([
            $calendarId,
            $encClient['cipher'], $encClient['iv'], $encClient['tag'], $encClient['salt'],
            $encSecret['cipher'], $encSecret['iv'], $encSecret['tag'], $encSecret['salt'],
            $encRefresh['cipher'], $encRefresh['iv'], $encRefresh['tag'], $encRefresh['salt'],
        ]);
        AuditLogger::log('google_calendar', 'save_config', ['calendar_id' => $calendarId]);
        return Response::json(['ok' => true]);
    }

    private function fetchAccessToken(array $cfg): ?string {
        if (strtolower((string)Config::get('GOOGLE_CALENDAR_MOCK', 'false')) === 'true') return 'mock_access_token';
        $payload = [
            'client_id' => $cfg['client_id'] ?? '',
            'client_secret' => $cfg['client_secret'] ?? '',
            'refresh_token' => $cfg['refresh_token'] ?? '',
            'grant_type' => 'refresh_token'
        ];
        $opts = ['http' => [
            'method' => 'POST', 'timeout' => 20, 'ignore_errors' => true,
            'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => http_build_query($payload)
        ]];
        $ctx = stream_context_create($opts);
        $res = @file_get_contents('https://oauth2.googleapis.com/token', false, $ctx);
        $json = json_decode($res ?: '{}', true);
        return (string)($json['access_token'] ?? '');
    }

    private function callCalendar(array $cfg, string $path, string $method = 'GET', ?array $payload = null): array {
        $access = $this->fetchAccessToken($cfg);
        if ($access === '') return ['status' => 401, 'body' => json_encode(['error' => 'no_access_token'])];
        if (strtolower((string)Config::get('GOOGLE_CALENDAR_MOCK', 'false')) === 'true') {
            if ($path === 'events' && $method === 'POST') return ['status' => 200, 'body' => json_encode(['id' => 'evt_mock_1'])];
        }
        $url = 'https://www.googleapis.com/calendar/v3/calendars/' . urlencode($cfg['calendar_id'] ?? 'primary') . '/' . ltrim($path, '/');
        $headers = [
            'Accept: application/json',
            'Content-Type: application/json',
            'Authorization: Bearer ' . $access
        ];
        $opts = ['http' => [ 'method' => $method, 'timeout' => 20, 'ignore_errors' => true, 'header' => implode("\r\n", $headers) ]];
        if ($payload !== null) $opts['http']['content'] = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $ctx = stream_context_create($opts);
        $res = @file_get_contents($url, false, $ctx);
        $status = null; global $http_response_header;
        if (is_array($http_response_header)) { foreach ($http_response_header as $h) { if (preg_match('/^HTTP\/\d+\.\d+\s+(\d+)/', $h, $m)) { $status = (int)$m[1]; break; } } }
        return ['status' => $status ?? 0, 'body' => $res === false ? null : $res];
    }

    public function createEvent(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo); $cfg = $this->loadConfig($pdo);
        if (empty($cfg['refresh_token'])) return Response::json(['error' => 'Configuração ausente'], 400);
        $summary = (string)($r->body['summary'] ?? 'Lembrete Financeiro');
        $desc = (string)($r->body['description'] ?? '');
        $start = (string)($r->body['start'] ?? '');
        $end = (string)($r->body['end'] ?? '');
        $reminderMinutes = (int)($r->body['reminder_minutes'] ?? 60);
        if ($start === '' || $end === '') return Response::json(['error' => 'start e end requeridos'], 422);
        $event = [
            'summary' => $summary,
            'description' => $desc,
            'start' => ['dateTime' => $start, 'timeZone' => 'America/Sao_Paulo'],
            'end' => ['dateTime' => $end, 'timeZone' => 'America/Sao_Paulo'],
            'reminders' => [ 'useDefault' => false, 'overrides' => [['method' => 'popup', 'minutes' => max(5, $reminderMinutes)]] ],
            'visibility' => 'private'
        ];
        $call = $this->callCalendar($cfg, 'events', 'POST', $event);
        $status = $call['status'];
        try { $pdo->prepare('INSERT INTO google_calendar_logs (operation, status, request_payload, response_body) VALUES (?,?,?,?)')->execute(['create_event', $status, json_encode($event), $call['body']]); } catch (\Throwable $e) {}
        if ($status >= 200 && $status < 300) { $json = json_decode($call['body'] ?? '{}', true); return Response::json(['data' => $json], 201); }
        return Response::json(['error' => 'google_error', 'status' => $status, 'response' => $call['body']], 502);
    }

    public function syncPayables(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo); $cfg = $this->loadConfig($pdo);
        if (empty($cfg['refresh_token'])) return Response::json(['error' => 'Configuração ausente'], 400);
        // Estratégia: aceitar payload de itens OU buscar da tabela se existir
        $items = is_array($r->body['items'] ?? null) ? $r->body['items'] : [];
        if (empty($items)) {
            try {
                // Tolerante: se não existir, ignora
                $rows = $pdo->query("SELECT id, descricao, valor, vencimento FROM contas_pagar WHERE vencimento >= CURRENT_DATE AND vencimento <= DATE_ADD(CURRENT_DATE, INTERVAL 30 DAY) LIMIT 50")->fetchAll();
                foreach ($rows as $rp) {
                    $items[] = [ 'summary' => 'Conta a pagar #' . $rp['id'], 'description' => (string)$rp['descricao'], 'date' => (string)$rp['vencimento'] ];
                }
            } catch (\Throwable $e) {}
        }
        $created = 0; $errors = [];
        foreach ($items as $it) {
            $date = (string)($it['date'] ?? ''); if ($date === '') continue;
            $start = $date . 'T09:00:00-03:00'; $end = $date . 'T10:00:00-03:00';
            $res = $this->createEvent(new Request(['body' => [
                'summary' => (string)($it['summary'] ?? 'Conta a pagar'),
                'description' => (string)($it['description'] ?? ''),
                'start' => $start,
                'end' => $end,
                'reminder_minutes' => (int)($r->body['reminder_minutes'] ?? 60)
            ]]));
            if ($res->status >= 200 && $res->status < 300) $created++; else $errors[] = $res->body;
        }
        AuditLogger::log('google_calendar', 'sync_payables', ['created' => $created]);
        return Response::json(['ok' => true, 'created' => $created, 'errors' => $errors]);
    }
}