<?php

declare(strict_types=1);

namespace App\Framework\Telemetry\Exporters;

use App\Framework\Telemetry\Config\ExporterConfig;
use App\Framework\Telemetry\ValueObjects\Event;
use App\Framework\Telemetry\ValueObjects\Metric;
use App\Framework\Telemetry\ValueObjects\Operation;
use DateTimeImmutable;
use RuntimeException;

/**
 * Exports telemetry data to log files
 */
final class FileExporter implements TelemetryExporterInterface
{
    /**
     * @var resource|null File handle for operations log
     */
    private $operationsHandle = null;

    /**
     * @var resource|null File handle for metrics log
     */
    private $metricsHandle = null;

    /**
     * @var resource|null File handle for events log
     */
    private $eventsHandle = null;

    /**
     * @param string $directory Directory where log files will be stored
     * @param bool $prettyPrint Whether to pretty-print JSON output
     */
    public function __construct(
        private readonly string $directory,
        private readonly bool $prettyPrint = false
    ) {
        $this->ensureDirectoryExists();
    }

    /**
     * Create from exporter config
     */
    public static function fromConfig(ExporterConfig $config): self
    {
        $directory = $config->getEndpoint();
        if (! $directory) {
            $directory = sys_get_temp_dir() . '/telemetry';
        }

        $prettyPrint = $config->getOption('pretty_print', false);

        return new self($directory, $prettyPrint);
    }

    /**
     * Export an operation
     */
    public function exportOperation(Operation $operation): void
    {
        if (! $operation->isCompleted()) {
            return; // Only export completed operations
        }

        $handle = $this->getOperationsHandle();
        $data = $operation->toArray();
        $this->writeToFile($handle, $data);
    }

    /**
     * Export a metric
     */
    public function exportMetric(Metric $metric): void
    {
        $handle = $this->getMetricsHandle();
        $data = $metric->toArray();
        $this->writeToFile($handle, $data);
    }

    /**
     * Export an event
     */
    public function exportEvent(Event $event): void
    {
        $handle = $this->getEventsHandle();
        $data = $event->toArray();
        $this->writeToFile($handle, $data);
    }

    /**
     * Flush any buffered telemetry data
     */
    public function flush(): void
    {
        if ($this->operationsHandle) {
            fflush($this->operationsHandle);
        }

        if ($this->metricsHandle) {
            fflush($this->metricsHandle);
        }

        if ($this->eventsHandle) {
            fflush($this->eventsHandle);
        }
    }

    /**
     * Close all file handles
     */
    public function close(): void
    {
        if ($this->operationsHandle) {
            fclose($this->operationsHandle);
            $this->operationsHandle = null;
        }

        if ($this->metricsHandle) {
            fclose($this->metricsHandle);
            $this->metricsHandle = null;
        }

        if ($this->eventsHandle) {
            fclose($this->eventsHandle);
            $this->eventsHandle = null;
        }
    }

    /**
     * Ensure the log directory exists
     */
    private function ensureDirectoryExists(): void
    {
        if (! is_dir($this->directory) && ! mkdir($this->directory, 0755, true)) {
            throw new RuntimeException("Failed to create directory: {$this->directory}");
        }
    }

    /**
     * Get the file handle for operations log
     *
     * @return resource
     */
    private function getOperationsHandle()
    {
        if ($this->operationsHandle === null) {
            $date = new DateTimeImmutable()->format('Y-m-d');
            $path = "{$this->directory}/operations-{$date}.jsonl";
            $this->operationsHandle = fopen($path, 'a');

            if ($this->operationsHandle === false) {
                throw new RuntimeException("Failed to open operations log file: {$path}");
            }
        }

        return $this->operationsHandle;
    }

    /**
     * Get the file handle for metrics log
     *
     * @return resource
     */
    private function getMetricsHandle()
    {
        if ($this->metricsHandle === null) {
            $date = new DateTimeImmutable()->format('Y-m-d');
            $path = "{$this->directory}/metrics-{$date}.jsonl";
            $this->metricsHandle = fopen($path, 'a');

            if ($this->metricsHandle === false) {
                throw new RuntimeException("Failed to open metrics log file: {$path}");
            }
        }

        return $this->metricsHandle;
    }

    /**
     * Get the file handle for events log
     *
     * @return resource
     */
    private function getEventsHandle()
    {
        if ($this->eventsHandle === null) {
            $date = new DateTimeImmutable()->format('Y-m-d');
            $path = "{$this->directory}/events-{$date}.jsonl";
            $this->eventsHandle = fopen($path, 'a');

            if ($this->eventsHandle === false) {
                throw new RuntimeException("Failed to open events log file: {$path}");
            }
        }

        return $this->eventsHandle;
    }

    /**
     * Write data to a file
     *
     * @param resource $handle File handle
     * @param array<string, mixed> $data Data to write
     */
    private function writeToFile($handle, array $data): void
    {
        $flags = $this->prettyPrint ? JSON_PRETTY_PRINT : 0;
        $json = json_encode($data, $flags) . PHP_EOL;

        if (fwrite($handle, $json) === false) {
            throw new RuntimeException("Failed to write to log file");
        }
    }

    /**
     * Destructor to ensure files are closed
     */
    public function __destruct()
    {
        $this->close();
    }
}
