<?php

namespace Fotogenic_AI;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

final class Price_Service {
    private static ?Price_Service $instance = null;
    private const CACHE_KEY_PREFIX = 'fotogenic_ai_price_tiers';
    private const CACHE_TTL = 43200;
    private const FLOAT_TIER_KEYS = [
        'imageGenerationHighQuality',
        'imageGenerationUltraHighQuality',
        'campaignSetGenerationCreditsPerImage',
        'highQualityEnhanceGeneration',
    ];
    private function __construct() {}

    public static function instance(): Price_Service {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function get_status(): array {
        $cached = $this->get_cached();
        if ( null === $cached ) {
            return $this->unavailable_status();
        }

        return $this->format_cached_response( $cached, false );
    }

    public function refresh(): array {
        $response = Api_Client::instance()->request( 'GET', 'price' );
        if ( is_wp_error( $response ) ) {
            $cached = $this->get_cached();
            if ( null !== $cached ) {
                $formatted = $this->format_cached_response( $cached, false );
                $formatted['error'] = true;
                $formatted['error_message'] = $response->get_error_message();

                return $formatted;
            }

            $unavailable                  = $this->unavailable_status();
            $unavailable['error']         = true;
            $unavailable['error_message'] = $response->get_error_message();

            return $unavailable;
        }

        $tiers = isset( $response['tiers'] ) && is_array( $response['tiers'] ) ? $response['tiers'] : [];
        $payload = [
            'tiers'               => $this->normalize_tiers( $tiers ),
            'authoritative_tiers' => $this->get_authoritative_tier_keys( $tiers ),
            'updated_at'          => time(),
        ];

        set_transient( $this->get_cache_key(), $payload, self::CACHE_TTL );

        return $this->format_cached_response( $payload, true );
    }

    /**
     * Return freshly confirmed Generate Images pricing without using defaults.
     *
     * @return array|\WP_Error
     */
    public function get_generate_images_pricing( string $quality = '2K' ) {
        $quality = strtoupper( trim( $quality ) );
		$tier_keys = [
			'1K' => 'imageGenerationHighQuality',
			'2K' => 'imageGenerationHighQuality',
			'4K' => 'imageGenerationUltraHighQuality',
		];
		$tier_key = $tier_keys[ $quality ] ?? '';
        if ( '' === $tier_key ) {
            return $this->quality_unavailable_error();
        }

        $status = $this->refresh();
        if ( ! $this->is_authoritative_status( $status, true ) ) {
            return $this->pricing_unavailable_error();
        }

        $cached = $this->get_cached();
        $authoritative_tiers = isset( $cached['authoritative_tiers'] ) && is_array( $cached['authoritative_tiers'] )
            ? $cached['authoritative_tiers']
            : [];
        if ( ! in_array( $tier_key, $authoritative_tiers, true ) ) {
            return $this->pricing_unavailable_error();
        }

        $tiers = isset( $status['tiers'] ) && is_array( $status['tiers'] ) ? $status['tiers'] : [];
        if ( ! array_key_exists( $tier_key, $tiers ) || ! is_numeric( $tiers[ $tier_key ] ) ) {
            return $this->pricing_unavailable_error();
        }

        $credits_per_image = (float) $tiers[ $tier_key ];
        if ( ! is_finite( $credits_per_image ) || $credits_per_image < 0 ) {
            return $this->pricing_unavailable_error();
        }

        return [
            'credits_per_image' => $credits_per_image,
            'quality'           => $quality,
            'last_updated'      => (int) ( $status['last_updated'] ?? 0 ),
            'fresh'             => true,
        ];
    }

    /**
     * Return fresh Campaign Set pricing or fail closed when either authority is unavailable.
     *
     * @param bool       $refresh       Whether to refresh from the price API first.
     * @param array|null $configuration Fresh current-account configuration.
     * @return array|\WP_Error
     */
    public function get_campaign_set_pricing( bool $refresh = true, ?array $configuration = null ) {
        $status = $refresh ? $this->refresh() : $this->get_status();
        if ( ! $this->is_authoritative_status( $status, $refresh ) ) {
            return $this->pricing_unavailable_error();
        }

        $cached = $this->get_cached();
        $authoritative_tiers = isset( $cached['authoritative_tiers'] ) && is_array( $cached['authoritative_tiers'] )
            ? $cached['authoritative_tiers']
            : [];
        if ( ! in_array( 'campaignSetGenerationCreditsPerImage', $authoritative_tiers, true ) ) {
            return $this->pricing_unavailable_error();
        }

        $tiers = isset( $status['tiers'] ) && is_array( $status['tiers'] ) ? $status['tiers'] : [];
        $price_key = 'campaignSetGenerationCreditsPerImage';
        if ( ! array_key_exists( $price_key, $tiers ) || ! is_numeric( $tiers[ $price_key ] ) ) {
            return $this->pricing_unavailable_error();
        }

        if ( null === $configuration ) {
            $configuration = ( new Service_Configuration() )->get_authoritative_configuration();
            if ( is_wp_error( $configuration ) ) {
                return $this->pricing_unavailable_error();
            }
        }
        $configuration_tiers = isset( $configuration['pricingTiers'] ) && is_array( $configuration['pricingTiers'] )
            ? $configuration['pricingTiers']
            : [];
        $output_limits = $configuration['operationalLimits']['workflows']['generateCampaignSet']['outputCount'] ?? null;
        if ( ! is_array( $output_limits ) ) {
            return $this->pricing_unavailable_error();
        }

        $configuration_price = $configuration_tiers[ $price_key ] ?? null;
        $minimum = $configuration_tiers['campaignSetGenerationMinImages'] ?? null;
        $maximum = $configuration_tiers['campaignSetGenerationMaxImages'] ?? null;
        $default = $configuration_tiers['campaignSetGenerationDefaultImages'] ?? null;
        $operational_minimum = $output_limits['minimum'] ?? null;
        $operational_maximum = $output_limits['maximum'] ?? null;
        if (
            ! is_numeric( $configuration_price )
            || ! is_int( $minimum )
            || ! is_int( $maximum )
            || ! is_int( $default )
            || ! is_int( $operational_minimum )
            || ! is_int( $operational_maximum )
        ) {
            return $this->pricing_unavailable_error();
        }

        $credits_per_image = (float) $tiers[ $price_key ];
        $configuration_price = (float) $configuration_price;
        if (
            ! is_finite( $credits_per_image )
            || ! is_finite( $configuration_price )
            || $credits_per_image < 0
            || $credits_per_image !== $configuration_price
            || $minimum < Campaign_Set_Recipe::MIN_OUTPUTS
            || $maximum > Campaign_Set_Recipe::MAX_OUTPUTS
            || $minimum > $maximum
            || $minimum !== $operational_minimum
            || $maximum !== $operational_maximum
            || $default < $minimum
            || $default > $maximum
        ) {
            return $this->pricing_unavailable_error();
        }

        return [
            'credits_per_image' => $credits_per_image,
            'min_count'         => $minimum,
            'max_count'         => $maximum,
            'default_count'     => $default,
            'last_updated'      => (int) ( $status['last_updated'] ?? 0 ),
            'fresh'             => ! empty( $status['fresh'] ),
        ];
    }

    /**
     * Return fresh Increase Resolution pricing or fail closed when unavailable.
     *
     * @param bool $refresh Whether to refresh from the API first.
     * @return array|\WP_Error
     */
    public function get_enhance_pricing( bool $refresh = true ) {
        $status = $refresh ? $this->refresh() : $this->get_status();
        if ( ! $this->is_authoritative_status( $status, $refresh ) ) {
            return $this->pricing_unavailable_error();
        }

        $tiers = isset( $status['tiers'] ) && is_array( $status['tiers'] ) ? $status['tiers'] : [];
        if ( ! array_key_exists( 'highQualityEnhanceGeneration', $tiers ) || ! is_numeric( $tiers['highQualityEnhanceGeneration'] ) ) {
            return $this->pricing_unavailable_error();
        }
        $credits = (float) $tiers['highQualityEnhanceGeneration'];
        if ( $credits < 0 ) {
            return $this->pricing_unavailable_error();
        }

        return [
            'credits'      => $credits,
            'last_updated' => (int) ( $status['last_updated'] ?? 0 ),
            'fresh'        => ! empty( $status['fresh'] ),
        ];
    }

    public function clear_cache(): void {
        delete_transient( $this->get_cache_key() );
    }

    private function get_cached(): ?array {
        $cached = get_transient( $this->get_cache_key() );
        if ( ! is_array( $cached ) || empty( $cached['updated_at'] ) ) {
            return null;
        }

        return $cached;
    }

    private function get_cache_key(): string {
        $options  = Settings::instance()->get_options();
        $env      = sanitize_key( (string) ( $options['api_environment'] ?? 'production' ) );
        $identity = sanitize_key( Auth_Session::instance()->get_cache_identity() );
        if ( '' === $env ) {
            $env = 'production';
        }
        if ( '' === $identity ) {
            $identity = 'guest';
        }

        return self::CACHE_KEY_PREFIX . '_' . $env . '_' . $identity;
    }

    private function unavailable_status(): array {
        return [
            'tiers'        => [],
            'last_updated' => 0,
            'age_seconds'  => 0,
            'age_minutes'  => 0,
            'available'    => false,
            'fresh'        => false,
        ];
    }

    private function format_cached_response( array $cached, bool $fresh ): array {
        $age_seconds = max( 0, time() - (int) $cached['updated_at'] );
        $tiers = isset( $cached['tiers'] ) && is_array( $cached['tiers'] ) ? $cached['tiers'] : [];

        return [
            'tiers'        => $tiers,
            'last_updated' => (int) $cached['updated_at'],
            'age_seconds'  => $age_seconds,
            'age_minutes'  => (int) floor( $age_seconds / 60 ),
            'available'    => ! empty( $tiers ),
            'fresh'        => $fresh,
        ];
    }

    private function normalize_tiers( array $tiers ): array {
        $normalized = [];
        foreach ( self::FLOAT_TIER_KEYS as $key ) {
            if ( isset( $tiers[ $key ] ) && is_numeric( $tiers[ $key ] ) && is_finite( (float) $tiers[ $key ] ) && (float) $tiers[ $key ] >= 0 ) {
                $normalized[ $key ] = (float) $tiers[ $key ];
            }
        }

        return $normalized;
    }

    /**
     * Record only values actually supplied and validated by the price endpoint.
     *
     * @return string[]
     */
    private function get_authoritative_tier_keys( array $tiers ): array {
        return array_values( array_intersect( self::FLOAT_TIER_KEYS, array_keys( $this->normalize_tiers( $tiers ) ) ) );
    }

    private function is_authoritative_status( array $status, bool $require_fresh ): bool {
        if ( ! empty( $status['error'] ) || empty( $status['available'] ) ) {
            return false;
        }
        if ( $require_fresh && empty( $status['fresh'] ) ) {
            return false;
        }
        return ! empty( $status['last_updated'] );
    }

    private function pricing_unavailable_error(): \WP_Error {
        return new \WP_Error(
            'fotogenic_ai_pricing_unavailable',
            __( 'Current workflow pricing is unavailable. Please refresh and try again.', 'fotogenic-ai' ),
            [ 'status' => 503 ]
        );
    }

    private function quality_unavailable_error(): \WP_Error {
        return new \WP_Error(
            'fotogenic_ai_quality_unavailable',
            __( 'The selected image quality is unavailable for Generate Images.', 'fotogenic-ai' ),
            [ 'status' => 409 ]
        );
    }
}
