<?php

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

class Niwa_AI_Model_Catalog {
    const MODELS_ENDPOINT = 'https://api.openai.com/v1/models';
    const API_MODELS_TRANSIENT_PREFIX = 'niwa_ai_openai_models_v6_';
    const API_MODELS_CACHE_TTL = 1800;

    public function get_models_for_mode( $mode, $api_key = '' ) {
        $mode = is_string( $mode ) ? trim( strtolower( $mode ) ) : 'api_key';
        if ( 'oauth' === $mode ) {
            return $this->get_codex_models();
        }

        $api_models = $this->get_api_key_models( $api_key );
        if ( ! empty( $api_models ) ) {
            return $api_models;
        }
        return $this->get_api_key_fallback_models();
    }

    public function get_founder_models_for_mode( $mode, $api_key = '' ) {
        $models = $this->get_models_for_mode( $mode, $api_key );
        if ( empty( $models ) || ! is_array( $models ) ) {
            return array();
        }

        return array_filter(
            $models,
            function( $label, $model_id ) {
                $model_id = strtolower( trim( (string) $model_id ) );
                return ! preg_match( '/^gpt-4([-.].+)?$/', $model_id );
            },
            ARRAY_FILTER_USE_BOTH
        );
    }

    public function get_default_chat_model( $mode ) {
        $mode = is_string( $mode ) ? trim( strtolower( $mode ) ) : 'api_key';
        return 'gpt-5.4-mini';
    }

    public function get_default_rlm_model( $mode ) {
        $mode = is_string( $mode ) ? trim( strtolower( $mode ) ) : 'api_key';
        if ( 'oauth' === $mode ) {
            return 'gpt-5.3-codex-spark';
        }
        return 'gpt-5.4-mini';
    }

    public function get_default_founder_model( $mode ) {
        $mode = is_string( $mode ) ? trim( strtolower( $mode ) ) : 'api_key';
        if ( 'oauth' === $mode ) {
            return 'gpt-5.4';
        }

        return 'gpt-5.4-mini';
    }

    public function get_all_founder_model_sets( $api_key = '' ) {
        return array(
            'codex'    => $this->get_founder_models_for_mode( 'oauth', $api_key ),
            'api_key'  => $this->get_founder_models_for_mode( 'api_key', $api_key ),
            'defaults' => array(
                'codex_chat'   => $this->get_default_founder_model( 'oauth' ),
                'api_key_chat' => $this->get_default_founder_model( 'api_key' ),
            ),
        );
    }

    public function is_valid_model_for_mode( $mode, $model ) {
        $mode  = is_string( $mode ) ? trim( strtolower( $mode ) ) : 'api_key';
        $model = is_string( $model ) ? trim( strtolower( $model ) ) : '';
        if ( '' === $model ) {
            return false;
        }

        if ( 'oauth' === $mode ) {
            $codex_models = array_keys( $this->get_codex_models() );
            return in_array( $model, $codex_models, true );
        }

        return $this->is_allowed_api_model( $model );
    }

    public function is_valid_founder_model_for_mode( $mode, $model, $api_key = '' ) {
        $mode  = is_string( $mode ) ? trim( strtolower( $mode ) ) : 'api_key';
        $model = is_string( $model ) ? trim( strtolower( $model ) ) : '';
        if ( '' === $model ) {
            return false;
        }

        $founder_models = array_keys( $this->get_founder_models_for_mode( $mode, $api_key ) );
        return in_array( $model, $founder_models, true );
    }

    public static function migrate_retired_selector( $model ) {
        $model_id = strtolower( trim( (string) $model ) );
        if ( preg_match( '/^gpt-5\.6-luna-(light|medium|high|xhigh)$/', $model_id, $matches ) ) {
            return 'gpt-5.6-terra-' . $matches[1];
        }

        return (string) $model;
    }

    public static function normalize_request_model( $model ) {
        $model_id = strtolower( trim( self::migrate_retired_selector( $model ) ) );
        $selectors = self::reasoning_model_selectors();
        if ( isset( $selectors[ $model_id ] ) ) {
            return $selectors[ $model_id ]['model'];
        }

        return (string) $model;
    }

    public static function reasoning_effort_for_model( $model ) {
        $model_id = strtolower( trim( self::migrate_retired_selector( $model ) ) );
        $selectors = self::reasoning_model_selectors();
        return isset( $selectors[ $model_id ] ) ? $selectors[ $model_id ]['effort'] : '';
    }

    public static function with_reasoning_effort( $model, $reasoning ) {
        $reasoning = is_array( $reasoning ) ? $reasoning : array();
        $effort    = self::reasoning_effort_for_model( $model );
        if ( '' !== $effort ) {
            $reasoning['effort'] = $effort;
        }

        return $reasoning;
    }

    private function get_codex_models() {
        return array_merge(
            $this->get_reasoning_models(),
            array(
                'gpt-5.4'             => 'gpt-5.4',
                'gpt-5.4-mini'        => 'gpt-5.4-mini',
                'gpt-5.3-codex-spark' => 'gpt-5.3-codex-spark',
                'gpt-5.3-codex'       => 'gpt-5.3-codex',
                'gpt-5.2-codex'       => 'gpt-5.2-codex',
                'gpt-5.1-codex'       => 'gpt-5.1-codex',
            )
        );
    }

    private function get_api_key_fallback_models() {
        return $this->get_allowed_api_selector_models();
    }

    private function get_allowed_api_selector_models() {
        return array_merge(
            $this->get_reasoning_models(),
            array(
                'gpt-4o'       => 'gpt-4o',
                'gpt-4o-mini'  => 'gpt-4o-mini',
                'gpt-5.1'      => 'gpt-5.1',
                'gpt-5.4'      => 'gpt-5.4',
                'gpt-5.4-mini' => 'gpt-5.4-mini',
            )
        );
    }

    private static function reasoning_model_selectors() {
        $selectors = array(
            'gpt-5.5-medium' => array( 'model' => 'gpt-5.5', 'effort' => 'medium', 'label' => 'gpt-5.5 medium' ),
            'gpt-5.5-high'   => array( 'model' => 'gpt-5.5', 'effort' => 'high', 'label' => 'gpt-5.5 high' ),
            'gpt-5.5-xhigh'  => array( 'model' => 'gpt-5.5', 'effort' => 'xhigh', 'label' => 'gpt-5.5 extra high' ),
        );

        $families = array(
            'gpt-5.6-terra' => 'gpt-5.6 Terra',
            'gpt-5.6-sol'   => 'gpt-5.6 Sol',
        );
        foreach ( $families as $request_model => $label ) {
            foreach ( array( 'light' => 'low', 'medium' => 'medium', 'high' => 'high', 'xhigh' => 'xhigh' ) as $suffix => $effort ) {
                $selectors[ $request_model . '-' . $suffix ] = array(
                    'model'  => $request_model,
                    'effort' => $effort,
                    'label'  => $label . ' ' . ( 'xhigh' === $suffix ? 'extra high' : $suffix ),
                );
            }
        }

        return $selectors;
    }

    private function get_reasoning_models() {
        $models = array();
        foreach ( self::reasoning_model_selectors() as $selector => $config ) {
            $models[ $selector ] = $config['label'];
        }

        return $models;
    }

    private function get_api_key_models( $api_key ) {
        $api_key = trim( (string) $api_key );
        if ( '' === $api_key ) {
            return array();
        }

        $transient_key = self::API_MODELS_TRANSIENT_PREFIX . hash( 'sha256', $api_key );
        $cached        = get_transient( $transient_key );
        if ( is_array( $cached ) && ! empty( $cached ) ) {
            return $cached;
        }

        $response = wp_remote_get(
            self::MODELS_ENDPOINT,
            array(
                'timeout' => 15,
                'headers' => array(
                    'Authorization' => 'Bearer ' . $api_key,
                    'Accept'        => 'application/json',
                ),
            )
        );
        if ( is_wp_error( $response ) ) {
            return array();
        }

        $status = wp_remote_retrieve_response_code( $response );
        if ( $status < 200 || $status >= 300 ) {
            return array();
        }

        $body = json_decode( wp_remote_retrieve_body( $response ), true );
        if ( ! is_array( $body ) || empty( $body['data'] ) || ! is_array( $body['data'] ) ) {
            return array();
        }

        $models = $this->get_reasoning_models();
        foreach ( $body['data'] as $item ) {
            $id = '';
            if ( is_array( $item ) && ! empty( $item['id'] ) && is_string( $item['id'] ) ) {
                $id = strtolower( trim( (string) $item['id'] ) );
            }
            if ( '' === $id || ! $this->is_allowed_api_model( $id ) ) {
                continue;
            }
            $models[ $id ] = $id;
        }

        if ( empty( $models ) ) {
            return array();
        }

        $sorted_ids = array_keys( $models );
        usort(
            $sorted_ids,
            function( $a, $b ) {
                $a_score = $this->model_rank_score( $a );
                $b_score = $this->model_rank_score( $b );
                if ( $a_score === $b_score ) {
                    return strcmp( $a, $b );
                }
                return ( $a_score > $b_score ) ? -1 : 1;
            }
        );

        $sorted_models = array();
        foreach ( $sorted_ids as $id ) {
            $sorted_models[ $id ] = $models[ $id ];
        }

        set_transient( $transient_key, $sorted_models, self::API_MODELS_CACHE_TTL );
        return $sorted_models;
    }

    private function is_allowed_api_model( $model_id ) {
        $model_id = strtolower( trim( (string) $model_id ) );
        if ( '' === $model_id ) {
            return false;
        }

        return isset( $this->get_allowed_api_selector_models()[ $model_id ] );
    }

    private function model_rank_score( $model_id ) {
        $model_id = strtolower( trim( (string) $model_id ) );
        $score    = 0;

        if ( preg_match( '/^gpt-(\d+)(?:\.(\d+))?/', $model_id, $m ) ) {
            $major = isset( $m[1] ) ? intval( $m[1] ) : 0;
            $minor = isset( $m[2] ) ? intval( $m[2] ) : 0;
            $score += ( $major * 100 ) + $minor;
        }

        if ( false !== strpos( $model_id, '-codex-spark' ) ) {
            $score += 9;
        } elseif ( false !== strpos( $model_id, '-codex' ) ) {
            $score += 7;
        } elseif ( false !== strpos( $model_id, '-mini' ) ) {
            $score -= 2;
        } elseif ( false !== strpos( $model_id, '-nano' ) ) {
            $score -= 4;
        }

        return $score;
    }
}
