<?php
/**
 * Version Manager
 *
 * @package Jeremias\WcLicensedProduct\Product
 */

declare(strict_types=1);

namespace Jeremias\WcLicensedProduct\Product;

use Jeremias\WcLicensedProduct\Installer;

/**
 * Manages product versions (CRUD operations)
 */
class VersionManager
{
    /**
     * Get version by ID
     */
    public function getVersionById(int $id): ?ProductVersion
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();
        $row = $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM {$tableName} WHERE id = %d", $id),
            ARRAY_A
        );

        return $row ? ProductVersion::fromArray($row) : null;
    }

    /**
     * Get all versions for a product
     */
    public function getVersionsByProduct(int $productId): array
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$tableName} WHERE product_id = %d ORDER BY major_version DESC, minor_version DESC, patch_version DESC",
                $productId
            ),
            ARRAY_A
        );

        return array_map(fn(array $row) => ProductVersion::fromArray($row), $rows ?: []);
    }

    /**
     * Get latest active version for a product
     */
    public function getLatestVersion(int $productId): ?ProductVersion
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();
        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$tableName} WHERE product_id = %d AND is_active = 1 ORDER BY major_version DESC, minor_version DESC, patch_version DESC LIMIT 1",
                $productId
            ),
            ARRAY_A
        );

        return $row ? ProductVersion::fromArray($row) : null;
    }

    /**
     * Get latest version for a specific major version
     */
    public function getLatestVersionForMajor(int $productId, int $majorVersion): ?ProductVersion
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();
        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$tableName} WHERE product_id = %d AND major_version = %d AND is_active = 1 ORDER BY minor_version DESC, patch_version DESC LIMIT 1",
                $productId,
                $majorVersion
            ),
            ARRAY_A
        );

        return $row ? ProductVersion::fromArray($row) : null;
    }

    /**
     * Create a new version
     *
     * @throws \InvalidArgumentException If file hash validation fails
     */
    public function createVersion(
        int $productId,
        string $version,
        ?string $releaseNotes = null,
        ?int $attachmentId = null,
        ?string $fileHash = null
    ): ?ProductVersion {
        global $wpdb;

        // Validate file hash if both attachment and hash are provided
        if ($attachmentId !== null && $attachmentId > 0 && $fileHash !== null && $fileHash !== '') {
            $validatedHash = $this->validateFileHash($attachmentId, $fileHash);
            if ($validatedHash === false) {
                return null;
            }
            $fileHash = $validatedHash;
        }

        $parsed = ProductVersion::parseVersion($version);

        $tableName = Installer::getVersionsTable();

        // Build data and formats arrays, handling null values properly
        $data = [
            'product_id' => $productId,
            'version' => $version,
            'major_version' => $parsed['major'],
            'minor_version' => $parsed['minor'],
            'patch_version' => $parsed['patch'],
            'release_notes' => $releaseNotes,
            'is_active' => 1,
        ];
        $formats = ['%d', '%s', '%d', '%d', '%d', '%s', '%d'];

        // Only include attachment_id if it's set
        if ($attachmentId !== null && $attachmentId > 0) {
            $data['attachment_id'] = $attachmentId;
            $formats[] = '%d';
        }

        // Only include file_hash if it's set
        if ($fileHash !== null && $fileHash !== '') {
            $data['file_hash'] = $fileHash;
            $formats[] = '%s';
        }

        $result = $wpdb->insert($tableName, $data, $formats);

        if ($result === false) {
            // Log error for debugging
            error_log('WC Licensed Product: Failed to create version - ' . $wpdb->last_error);
            return null;
        }

        return $this->getVersionById((int) $wpdb->insert_id);
    }

    /**
     * Validate file hash against attachment
     *
     * @return string|false The validated hash (lowercase) or false on mismatch
     * @throws \InvalidArgumentException If hash doesn't match
     */
    private function validateFileHash(int $attachmentId, string $providedHash): string|false
    {
        $filePath = get_attached_file($attachmentId);
        if (!$filePath || !file_exists($filePath)) {
            throw new \InvalidArgumentException(
                __('Attachment file not found.', 'wc-licensed-product')
            );
        }

        $calculatedHash = hash_file('sha256', $filePath);
        $providedHash = strtolower(trim($providedHash));

        if (!hash_equals($calculatedHash, $providedHash)) {
            throw new \InvalidArgumentException(
                sprintf(
                    /* translators: 1: provided hash, 2: calculated hash */
                    __('File checksum does not match. Expected: %1$s, Got: %2$s', 'wc-licensed-product'),
                    $providedHash,
                    $calculatedHash
                )
            );
        }

        return $calculatedHash;
    }

    /**
     * Update a version
     */
    public function updateVersion(
        int $versionId,
        ?string $releaseNotes = null,
        ?bool $isActive = null,
        ?int $attachmentId = null
    ): bool {
        global $wpdb;

        $data = [];
        $formats = [];

        if ($releaseNotes !== null) {
            $data['release_notes'] = $releaseNotes;
            $formats[] = '%s';
        }

        if ($isActive !== null) {
            $data['is_active'] = $isActive ? 1 : 0;
            $formats[] = '%d';
        }

        if ($attachmentId !== null) {
            if ($attachmentId > 0) {
                $data['attachment_id'] = $attachmentId;
                $formats[] = '%d';
            } else {
                // Set to NULL using raw SQL instead of adding to $data
                global $wpdb;
                $tableName = Installer::getVersionsTable();
                $wpdb->query(
                    $wpdb->prepare(
                        "UPDATE {$tableName} SET attachment_id = NULL WHERE id = %d",
                        $versionId
                    )
                );
            }
        }

        if (empty($data)) {
            return true;
        }

        $tableName = Installer::getVersionsTable();
        $result = $wpdb->update(
            $tableName,
            $data,
            ['id' => $versionId],
            $formats,
            ['%d']
        );

        return $result !== false;
    }

    /**
     * Delete a version
     */
    public function deleteVersion(int $versionId): bool
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();
        $result = $wpdb->delete(
            $tableName,
            ['id' => $versionId],
            ['%d']
        );

        return $result !== false;
    }

    /**
     * Check if version exists for product
     */
    public function versionExists(int $productId, string $version): bool
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();
        $count = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM {$tableName} WHERE product_id = %d AND version = %s",
                $productId,
                $version
            )
        );

        return (int) $count > 0;
    }

    /**
     * Increment download count for a version
     */
    public function incrementDownloadCount(int $versionId): bool
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();
        $result = $wpdb->query(
            $wpdb->prepare(
                "UPDATE {$tableName} SET download_count = download_count + 1 WHERE id = %d",
                $versionId
            )
        );

        return $result !== false;
    }

    /**
     * Get total download count across all versions
     */
    public function getTotalDownloadCount(): int
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();
        $count = $wpdb->get_var("SELECT COALESCE(SUM(download_count), 0) FROM {$tableName}");

        return (int) $count;
    }

    /**
     * Get download statistics per product
     */
    public function getDownloadStatistics(): array
    {
        global $wpdb;

        $tableName = Installer::getVersionsTable();

        // Get total downloads
        $totalDownloads = $this->getTotalDownloadCount();

        // Get downloads per product (top 10)
        $byProduct = $wpdb->get_results(
            "SELECT product_id, SUM(download_count) as downloads
             FROM {$tableName}
             GROUP BY product_id
             ORDER BY downloads DESC
             LIMIT 10",
            ARRAY_A
        );

        // Get downloads per version (top 10)
        $byVersion = $wpdb->get_results(
            "SELECT id, product_id, version, download_count
             FROM {$tableName}
             WHERE download_count > 0
             ORDER BY download_count DESC
             LIMIT 10",
            ARRAY_A
        );

        // Enrich product data with names
        $productsWithNames = [];
        foreach ($byProduct ?: [] as $row) {
            $product = wc_get_product((int) $row['product_id']);
            $productsWithNames[] = [
                'product_id' => (int) $row['product_id'],
                'product_name' => $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product'),
                'downloads' => (int) $row['downloads'],
            ];
        }

        // Enrich version data with product names
        $versionsWithNames = [];
        foreach ($byVersion ?: [] as $row) {
            $product = wc_get_product((int) $row['product_id']);
            $versionsWithNames[] = [
                'version_id' => (int) $row['id'],
                'product_id' => (int) $row['product_id'],
                'product_name' => $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product'),
                'version' => $row['version'],
                'downloads' => (int) $row['download_count'],
            ];
        }

        return [
            'total' => $totalDownloads,
            'by_product' => $productsWithNames,
            'by_version' => $versionsWithNames,
        ];
    }
}
