<?php

declare(strict_types=1);

require_once __DIR__ . '/DowngradeCompatibilityReport.php';
require_once __DIR__ . '/LicenseRemediationState.php';

final class MyronLicenseRepository
{
    public function __construct(private readonly PDO $pdo)
    {
    }

    /**
     * @return array<string, mixed>|null
     */
    public function current(): ?array
    {
        $statement = $this->pdo->query('SELECT * FROM license_activation_state ORDER BY updated_at DESC LIMIT 1');
        $row = $statement ? $statement->fetch(PDO::FETCH_ASSOC) : false;
        return is_array($row) ? $row : null;
    }

    public function currentSnapshot(): MyronEntitlementSnapshot
    {
        $row = $this->current();
        if ($row === null) {
            return MyronEntitlementSnapshot::missing();
        }

        return MyronEntitlementSnapshot::fromJson((string) ($row['entitlement_json'] ?? ''));
    }

    public function currentRemediationState(): MyronLicenseRemediationState
    {
        $row = $this->current();
        $snapshot = $row === null
            ? MyronEntitlementSnapshot::missing()
            : MyronEntitlementSnapshot::fromJson((string) ($row['entitlement_json'] ?? ''));

        return MyronLicenseRemediationState::fromSnapshot(
            $snapshot,
            (string) ($row['last_validation_status'] ?? ''),
            (string) ($row['last_validation_reason'] ?? '')
        );
    }

    public function downgradeCompatibilityReport(string $targetTier): MyronDowngradeCompatibilityReport
    {
        return MyronDowngradeCompatibilityReport::build($this->currentSnapshot(), $targetTier);
    }

    /**
     * @param array<string, mixed> $payload
     */
    public function saveActivationPayload(array $payload, string $licenseKey, string $deploymentKey = ''): MyronEntitlementSnapshot
    {
        $snapshot = MyronEntitlementSnapshot::fromArray($payload);
        $now = myronNow();
        $licenseMask = trim((string) ($payload['licenseMask'] ?? $payload['license_mask'] ?? ''));
        if ($licenseMask === '') {
            $licenseMask = self::maskKey($licenseKey);
        }
        $deploymentMask = trim((string) ($payload['deploymentKeyMask'] ?? $payload['deployment_key_mask'] ?? ''));
        if ($deploymentMask === '' && $deploymentKey !== '') {
            $deploymentMask = self::maskKey($deploymentKey);
        }
        $activeSkusJson = json_encode($snapshot->activeSkus, JSON_UNESCAPED_SLASHES);
        $apiVersion = substr(trim($snapshot->apiVersion), 0, 8);
        $skuModel = substr(trim((string) ($payload['skuModel'] ?? $payload['sku_model'] ?? ($apiVersion === 'v2' ? 'v2' : 'v1'))), 0, 16);

        $statement = $this->pdo->prepare(
            'REPLACE INTO license_activation_state (
                id, activation_id, plan, status, license_fingerprint, license_mask,
                deployment_key_fingerprint, deployment_key_mask, deployment_group, environment,
                entitlement_json, sku_model, active_skus_json, api_version_negotiated,
                last_validation_status, last_validation_reason,
                last_validated_at, activated_at, updated_at
            ) VALUES (
                1, :activation_id, :plan, :status, :license_fingerprint, :license_mask,
                :deployment_key_fingerprint, :deployment_key_mask, :deployment_group, :environment,
                :entitlement_json, :sku_model, :active_skus_json, :api_version_negotiated,
                :last_validation_status, :last_validation_reason,
                :last_validated_at, :activated_at, :updated_at
            )'
        );
        $statement->execute([
            ':activation_id' => substr(trim((string) ($payload['activationId'] ?? $payload['activation_id'] ?? '')), 0, 120),
            ':plan' => $snapshot->plan,
            ':status' => $snapshot->status,
            ':license_fingerprint' => self::fingerprint($licenseKey),
            ':license_mask' => $licenseMask,
            ':deployment_key_fingerprint' => $deploymentKey !== '' ? self::fingerprint($deploymentKey) : '',
            ':deployment_key_mask' => $deploymentMask,
            ':deployment_group' => substr($snapshot->deploymentGroup, 0, 120),
            ':environment' => $snapshot->environment,
            ':entitlement_json' => $snapshot->toJson(),
            ':sku_model' => $skuModel !== '' ? $skuModel : 'v1',
            ':active_skus_json' => is_string($activeSkusJson) ? $activeSkusJson : '[]',
            ':api_version_negotiated' => $apiVersion !== '' ? $apiVersion : 'v1',
            ':last_validation_status' => 'ok',
            ':last_validation_reason' => '',
            ':last_validated_at' => $snapshot->lastValidatedAt ?? $now,
            ':activated_at' => $now,
            ':updated_at' => $now,
        ]);

        return $snapshot;
    }

    public function recordValidationFailure(string $reasonCode, bool $preserveSnapshot = true): void
    {
        $row = $this->current();
        if ($row === null) {
            $snapshot = MyronEntitlementSnapshot::missing();
            $statement = $this->pdo->prepare(
                'REPLACE INTO license_activation_state (
                    id, activation_id, plan, status, license_fingerprint, license_mask,
                    deployment_key_fingerprint, deployment_key_mask, deployment_group, environment,
                    entitlement_json, sku_model, active_skus_json, api_version_negotiated,
                    last_validation_status, last_validation_reason,
                    last_validated_at, activated_at, updated_at
                ) VALUES (
                    1, \'\', :plan, :status, \'\', \'\', \'\', \'\', \'\', :environment,
                    :entitlement_json, \'v1\', \'[]\', \'v1\', \'failed\', :reason, NULL, NULL, :updated_at
                )'
            );
            $statement->execute([
                ':plan' => $snapshot->plan,
                ':status' => $snapshot->status,
                ':environment' => $snapshot->environment,
                ':entitlement_json' => $snapshot->toJson(),
                ':reason' => substr($reasonCode, 0, 120),
                ':updated_at' => myronNow(),
            ]);
            return;
        }

        $status = $preserveSnapshot
            ? (string) ($row['status'] ?? MyronLicenseStatus::STALE)
            : MyronLicenseStatus::INVALID;
        $statement = $this->pdo->prepare(
            'UPDATE license_activation_state
             SET status = :status,
                 last_validation_status = \'failed\',
                 last_validation_reason = :reason,
                 updated_at = :updated_at
             WHERE id = 1'
        );
        $statement->execute([
            ':status' => MyronLicenseStatus::normalize($status),
            ':reason' => substr(trim($reasonCode), 0, 120),
            ':updated_at' => myronNow(),
        ]);
    }

    public function clearActivationState(): void
    {
        $this->pdo->exec('DELETE FROM license_activation_state WHERE id = 1');
    }

    public static function fingerprint(string $key): string
    {
        $trimmed = trim($key);
        return $trimmed !== '' ? hash('sha256', $trimmed) : '';
    }

    public static function maskKey(string $key): string
    {
        $clean = preg_replace('/[^A-Za-z0-9]/', '', trim($key)) ?? '';
        if (strlen($clean) <= 8) {
            return $clean !== '' ? str_repeat('*', max(4, strlen($clean))) : '';
        }

        return substr($clean, 0, 6) . '-...' . substr($clean, -4);
    }
}
