<?php

declare(strict_types=1);

namespace App\Modules\Developer;

/**
 * SyncLogService — Synchronization History Manager
 *
 * Stores and retrieves synchronization log entries using a JSON file.
 * No MySQL table required — keeps developer tooling self-contained.
 *
 * Log file: storage/developer/sync_log.json
 * Format: Array of log entry objects, newest first.
 *
 * @package Inveetaire
 * @ref     DX002B
 */
class SyncLogService
{
    private string $logFile;

    public function __construct()
    {
        $dir           = ROOT_PATH . '/storage/developer';
        $this->logFile = $dir . '/sync_log.json';

        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Read
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Get all log entries, newest first.
     *
     * @return list<array>
     */
    public function getAll(): array
    {
        if (!file_exists($this->logFile)) {
            return [];
        }

        $json    = @file_get_contents($this->logFile);
        $entries = json_decode($json ?: '[]', true);

        return is_array($entries) ? $entries : [];
    }

    /**
     * Get the most recent N entries.
     */
    public function getRecent(int $limit = 20): array
    {
        return array_slice($this->getAll(), 0, $limit);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Write
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Append a new log entry. Prepends (newest first). Caps at 200 entries.
     *
     * @param array $entry Must contain at minimum: direction, is_dry_run, result
     */
    public function append(array $entry): void
    {
        $entries = $this->getAll();

        // Normalize required fields with safe defaults
        $normalized = [
            'id'               => $entry['id']               ?? ('sync_' . date('Ymd_His') . '_' . substr(md5(uniqid()), 0, 6)),
            'timestamp'        => $entry['timestamp']        ?? date('Y-m-d H:i:s'),
            'user'             => $entry['user']             ?? 'super_admin',
            'direction'        => $entry['direction']        ?? 'mysql_to_supabase',
            'is_dry_run'       => (bool)($entry['is_dry_run'] ?? false),
            'tables_affected'  => (int)($entry['tables_affected']  ?? 0),
            'columns_affected' => (int)($entry['columns_affected'] ?? 0),
            'sql_statements'   => (int)($entry['sql_statements']   ?? 0),
            'result'           => $entry['result']           ?? 'unknown',
            'duration_ms'      => (int)($entry['duration_ms']      ?? 0),
            'execution_mode'   => $entry['execution_mode']   ?? 'sql_export',
            'error_message'    => $entry['error_message']    ?? null,
        ];

        array_unshift($entries, $normalized); // prepend (newest first)

        // Cap at 200 entries
        if (count($entries) > 200) {
            $entries = array_slice($entries, 0, 200);
        }

        @file_put_contents($this->logFile, json_encode($entries, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
    }

    /**
     * Clear all log entries.
     */
    public function clear(): void
    {
        @file_put_contents($this->logFile, '[]');
    }

    /**
     * Get the last sync timestamp.
     */
    public function getLastSyncTime(): ?string
    {
        $entries = $this->getAll();
        if (empty($entries)) {
            return null;
        }

        foreach ($entries as $entry) {
            if (($entry['is_dry_run'] ?? false) === false && ($entry['result'] ?? '') === 'success') {
                return $entry['timestamp'] ?? null;
            }
        }

        return null;
    }

    /**
     * Get total sync count (excluding dry runs).
     */
    public function getTotalSyncs(): int
    {
        return count(array_filter($this->getAll(), static fn($e) => !($e['is_dry_run'] ?? false)));
    }

    /**
     * Get the most recent mirror status from logs.
     */
    public function getLastStatus(): string
    {
        $entries = $this->getAll();
        foreach ($entries as $entry) {
            if (!($entry['is_dry_run'] ?? false)) {
                return $entry['result'] ?? 'unknown';
            }
        }
        return 'never';
    }
}
