<?php

declare(strict_types=1);

namespace App\Framework\Deployment\Pipeline\Services;

use App\Framework\Database\Connection\ConnectionInterface;
use App\Framework\Deployment\Pipeline\ValueObjects\DeploymentEnvironment;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineHistoryEntry;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineId;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineResult;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineStatus;
use App\Framework\Logging\Logger;

/**
 * Service for managing persistent deployment history
 *
 * Stores completed pipeline executions in database for:
 * - Deployment audit trail
 * - Success/failure analytics
 * - Rollback history
 * - Performance tracking
 */
final readonly class PipelineHistoryService
{
    public function __construct(
        private ConnectionInterface $connection,
        private Logger $logger
    ) {}

    /**
     * Record completed pipeline execution
     */
    public function recordPipeline(
        PipelineResult $result,
        \DateTimeImmutable $startedAt,
        \DateTimeImmutable $completedAt
    ): void {
        $entry = PipelineHistoryEntry::fromPipelineResult($result, $startedAt, $completedAt);

        $this->logger->info('Recording pipeline execution in history', [
            'pipeline_id' => $entry->pipelineId->value,
            'environment' => $entry->environment->value,
            'status' => $entry->status->value,
            'duration_ms' => $entry->totalDuration->toMilliseconds()
        ]);

        $sql = <<<SQL
            INSERT INTO pipeline_history (
                pipeline_id, environment, status, stages_data, total_duration_ms,
                error, was_rolled_back, failed_stage, started_at, completed_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        SQL;

        $data = $entry->toArray();

        $this->connection->execute($sql, [
            $data['pipeline_id'],
            $data['environment'],
            $data['status'],
            $data['stages_data'],
            $data['total_duration_ms'],
            $data['error'],
            $data['was_rolled_back'] ? 1 : 0,
            $data['failed_stage'],
            $data['started_at'],
            $data['completed_at']
        ]);
    }

    /**
     * Get pipeline history entry by ID
     */
    public function getById(PipelineId $pipelineId): ?PipelineHistoryEntry
    {
        $sql = 'SELECT * FROM pipeline_history WHERE pipeline_id = ? LIMIT 1';

        $result = $this->connection->query($sql, [$pipelineId->value]);

        if (empty($result)) {
            return null;
        }

        return PipelineHistoryEntry::fromArray($result[0]);
    }

    /**
     * Get recent pipeline executions
     *
     * @return array<PipelineHistoryEntry>
     */
    public function getRecent(int $limit = 50, ?DeploymentEnvironment $environment = null): array
    {
        $sql = 'SELECT * FROM pipeline_history';
        $params = [];

        if ($environment !== null) {
            $sql .= ' WHERE environment = ?';
            $params[] = $environment->value;
        }

        $sql .= ' ORDER BY completed_at DESC LIMIT ?';
        $params[] = $limit;

        $results = $this->connection->query($sql, $params);

        return array_map(
            fn($row) => PipelineHistoryEntry::fromArray($row),
            $results
        );
    }

    /**
     * Get pipeline executions by status
     *
     * @return array<PipelineHistoryEntry>
     */
    public function getByStatus(
        PipelineStatus $status,
        int $limit = 50,
        ?DeploymentEnvironment $environment = null
    ): array {
        $sql = 'SELECT * FROM pipeline_history WHERE status = ?';
        $params = [$status->value];

        if ($environment !== null) {
            $sql .= ' AND environment = ?';
            $params[] = $environment->value;
        }

        $sql .= ' ORDER BY completed_at DESC LIMIT ?';
        $params[] = $limit;

        $results = $this->connection->query($sql, $params);

        return array_map(
            fn($row) => PipelineHistoryEntry::fromArray($row),
            $results
        );
    }

    /**
     * Get deployment statistics
     */
    public function getStatistics(?DeploymentEnvironment $environment = null): array
    {
        $whereClause = $environment !== null ? 'WHERE environment = ?' : '';
        $params = $environment !== null ? [$environment->value] : [];

        // Total deployments
        $totalSql = "SELECT COUNT(*) as total FROM pipeline_history {$whereClause}";
        $totalResult = $this->connection->query($totalSql, $params);
        $total = (int) $totalResult[0]['total'];

        // Success count
        $successSql = "SELECT COUNT(*) as success FROM pipeline_history {$whereClause}";
        if ($environment !== null) {
            $successSql .= " AND status = 'success'";
        } else {
            $successSql .= " WHERE status = 'success'";
        }
        $successResult = $this->connection->query($successSql, $params);
        $success = (int) $successResult[0]['success'];

        // Failed count
        $failedSql = "SELECT COUNT(*) as failed FROM pipeline_history {$whereClause}";
        if ($environment !== null) {
            $failedSql .= " AND status = 'failed'";
        } else {
            $failedSql .= " WHERE status = 'failed'";
        }
        $failedResult = $this->connection->query($failedSql, $params);
        $failed = (int) $failedResult[0]['failed'];

        // Rolled back count
        $rolledBackSql = "SELECT COUNT(*) as rolled_back FROM pipeline_history WHERE was_rolled_back = 1";
        if ($environment !== null) {
            $rolledBackSql .= " AND environment = ?";
        }
        $rolledBackResult = $this->connection->query($rolledBackSql, $params);
        $rolledBack = (int) $rolledBackResult[0]['rolled_back'];

        // Average duration
        $avgDurationSql = "SELECT AVG(total_duration_ms) as avg_duration FROM pipeline_history {$whereClause}";
        $avgDurationResult = $this->connection->query($avgDurationSql, $params);
        $avgDuration = (int) ($avgDurationResult[0]['avg_duration'] ?? 0);

        // Success rate
        $successRate = $total > 0 ? ($success / $total) * 100 : 0;

        return [
            'total_deployments' => $total,
            'successful' => $success,
            'failed' => $failed,
            'rolled_back' => $rolledBack,
            'success_rate' => round($successRate, 2),
            'average_duration_ms' => $avgDuration,
            'environment' => $environment?->value ?? 'all'
        ];
    }

    /**
     * Get deployments within date range
     *
     * @return array<PipelineHistoryEntry>
     */
    public function getByDateRange(
        \DateTimeImmutable $from,
        \DateTimeImmutable $to,
        ?DeploymentEnvironment $environment = null
    ): array {
        $sql = 'SELECT * FROM pipeline_history WHERE completed_at BETWEEN ? AND ?';
        $params = [
            $from->format('Y-m-d H:i:s'),
            $to->format('Y-m-d H:i:s')
        ];

        if ($environment !== null) {
            $sql .= ' AND environment = ?';
            $params[] = $environment->value;
        }

        $sql .= ' ORDER BY completed_at DESC';

        $results = $this->connection->query($sql, $params);

        return array_map(
            fn($row) => PipelineHistoryEntry::fromArray($row),
            $results
        );
    }

    /**
     * Delete old history entries
     *
     * @return int Number of deleted entries
     */
    public function pruneOldEntries(\DateTimeImmutable $olderThan): int
    {
        $sql = 'DELETE FROM pipeline_history WHERE completed_at < ?';

        $this->connection->execute($sql, [$olderThan->format('Y-m-d H:i:s')]);

        $affectedRows = $this->connection->affectedRows();

        $this->logger->info('Pruned old pipeline history entries', [
            'deleted_count' => $affectedRows,
            'older_than' => $olderThan->format('Y-m-d H:i:s')
        ]);

        return $affectedRows;
    }
}
