<?php

if(!defined('SOFTACULOUS')){
	die('Hacking Attempt');
}

require_once(__DIR__ . '/class_ai_file_handler.php');

class AISettings {
	private $file_path;
	private $data;

	public function __construct(string $username){
		$dir = AIFileHandler::get_ai_base_dir($username);
		$this->file_path = $dir . '/settings.json.php';

		$json_data = AIFileHandler::read($this->file_path);
		if($json_data === null){
			$this->data = ['providers' => []];
		}else{
			$this->data = $json_data;
		}
	}

	public function get_connected_providers(): array{
		return $this->data['providers'] ?? [];
	}

	public function is_provider_connected(string $provider_id): bool{
		if(!empty($this->data['providers'][$provider_id])) return true;
		$no_key = ['opencode_zen'];
		if(in_array($provider_id, $no_key)) return true;
		if(strpos($provider_id, 'custom:') === 0 && !empty($this->data['providers'][$provider_id])) return true;
		return false;
	}

	public function get_provider_config(string $provider_id): ?array{
		return $this->data['providers'][$provider_id] ?? null;
	}

	public function get_api_key(string $provider_id): string{
		$config = $this->data['providers'][$provider_id] ?? [];
		$key = $config['api_key'] ?? '';
		if(empty($key)){
			$no_key = ['opencode_zen'];
			if(in_array($provider_id, $no_key)) return 'public';
			if(!empty($config['auth_type']) && $config['auth_type'] === 'none') return 'public';
		}
		return $key;
	}

	public function save_provider(string $provider_id, array $config): void{
		$config['connected_at'] = $config['connected_at'] ?? time();
		$this->data['providers'][$provider_id] = $config;
		$this->save();
	}

	public function delete_provider(string $provider_id): void{
		unset($this->data['providers'][$provider_id]);
		$this->save();
	}

	public function get_all_providers(): array{
		require_once(__DIR__ . '/../providers/interface_ai_provider.php');
		require_once(__DIR__ . '/../providers/class_providers.php');

		$builtin = [
			['id' => 'openai', 'name' => 'OpenAI', 'logo' => 'openai', 'auth_type' => 'api_key',
				'models' => ['gpt-4o' => 'GPT-4o', 'gpt-4o-mini' => 'GPT-4o Mini', 'gpt-4-turbo' => 'GPT-4 Turbo']],
		['id' => 'anthropic', 'name' => 'Anthropic', 'logo' => 'anthropic', 'auth_type' => 'api_key',
			'models' => $this->get_flat_models('anthropic', $this->data['providers']['anthropic'] ?? [])],
			['id' => 'google', 'name' => 'Google', 'logo' => 'google', 'auth_type' => 'api_key',
				'models' => ['gemini-2.5-pro' => 'Gemini 2.5 Pro', 'gemini-2.5-flash' => 'Gemini 2.5 Flash', 'gemini-1.5-pro' => 'Gemini 1.5 Pro']],
			['id' => 'openrouter', 'name' => 'OpenRouter', 'logo' => 'openrouter', 'auth_type' => 'api_key',
				'models' => ['anthropic/claude-sonnet-4.5' => 'Claude Sonnet 4.5', 'openai/gpt-4o' => 'GPT-4o', 'google/gemini-2.5-pro' => 'Gemini 2.5 Pro']],
		['id' => 'groq', 'name' => 'Groq', 'logo' => 'groq', 'auth_type' => 'api_key',
			'base_url' => 'https://api.groq.com/openai/v1',
			'models' => ['llama-3.3-70b-versatile' => 'Llama 3.3 70B', 'llama-3.1-8b-instant' => 'Llama 3.1 8B']],
		['id' => 'deepseek', 'name' => 'DeepSeek', 'logo' => 'deepseek', 'auth_type' => 'api_key',
			'base_url' => 'https://api.deepseek.com/v1',
			'models' => ['deepseek-chat' => 'DeepSeek Chat', 'deepseek-coder' => 'DeepSeek Coder']],
		['id' => 'together', 'name' => 'Together AI', 'logo' => 'together', 'auth_type' => 'api_key',
			'models' => ['meta-llama/Llama-3-70b' => 'Llama 3 70B', 'deepseek-ai/DeepSeek-R1' => 'DeepSeek R1']],
		['id' => 'xai', 'name' => 'xAI (Grok)', 'logo' => 'xai', 'auth_type' => 'api_key',
			'base_url' => 'https://api.x.ai/v1',
			'models' => ['grok-4.3' => 'Grok 4.3', 'grok-4.20-0309-reasoning' => 'Grok 4.20 (Reasoning)']],
		['id' => 'mistral', 'name' => 'Mistral AI', 'logo' => 'mistral', 'auth_type' => 'api_key',
			'base_url' => 'https://api.mistral.ai/v1',
			'models' => ['mistral-large-latest' => 'Mistral Large', 'codestral-latest' => 'Codestral']],
		['id' => 'perplexity', 'name' => 'Perplexity', 'logo' => 'perplexity', 'auth_type' => 'api_key',
			'base_url' => 'https://api.perplexity.ai',
			'models' => ['sonar-pro' => 'Sonar Pro', 'sonar-reasoning' => 'Sonar Reasoning']],
			['id' => 'ollama', 'name' => 'Ollama (Local)', 'logo' => 'ollama', 'auth_type' => 'none',
				'models' => ['llama3' => 'Llama 3', 'codellama' => 'Code Llama', 'mistral' => 'Mistral']],
			['id' => 'ollama_cloud', 'name' => 'Ollama Cloud', 'logo' => 'ollama', 'auth_type' => 'api_key',
				'base_url' => 'https://api.ollama.com/v1',
				'models' => $this->get_flat_models('ollama_cloud')],
			['id' => 'minimax', 'name' => 'MiniMax', 'logo' => 'minimax', 'auth_type' => 'api_key',
				'base_url' => 'https://api.minimax.chat/v1',
				'models' => ['MiniMax-Text-01' => 'MiniMax-Text-01', 'MiniMax-M1' => 'MiniMax-M1']],
			['id' => 'opencode_zen', 'name' => 'OpenCode Zen (Free)', 'logo' => 'opencode', 'auth_type' => 'none',
				'base_url' => 'https://opencode.ai/zen/v1',
				'models' => $this->get_flat_models('opencode_zen'),
				'no_key' => true],
			['id' => 'opencode_zen_premium', 'name' => 'OpenCode Zen (Premium)', 'logo' => 'opencode', 'auth_type' => 'api_key',
				'base_url' => 'https://opencode.ai/zen/v1',
				'extra_headers' => ['HTTP-Referer: https://opencode.ai/', 'X-Title: opencode'],
				'models' => $this->get_flat_models('opencode_zen_premium'),
				'no_key' => false],
			['id' => 'custom', 'name' => 'Custom Provider (OpenAI Compatible)', 'logo' => 'custom', 'auth_type' => 'api_key',
				'is_custom_type' => true, 'models' => [], 'no_key' => false],
		];

		foreach($builtin as &$p){
			if(!empty($p['is_custom_type'])){
				$p['connected'] = false;
			}else{
				$p['connected'] = $this->is_provider_connected($p['id']);
			}
			// Auto-detect models from models.dev for this provider
			if(!empty($p['id']) && empty($p['is_custom_type'])){
				$detected = $this->detect_models_from_dev($p['id']);
				if(!empty($detected)){
					// Merge: keep existing models, add new ones from models.dev
					$p['models'] = array_merge($p['models'] ?? [], $detected);
				}
			}
		}

		foreach($this->data['providers'] as $pid => $config){
			if(strpos($pid, 'custom:') === 0 && empty($config['_from_filter'])){
				$entry = [
					'id' => $pid,
					'name' => $config['name'] ?? 'Custom Provider',
					'logo' => 'custom',
					'auth_type' => 'api_key',
					'models' => $config['models'] ?? [],
					'connected' => true,
					'is_custom' => true
				];
				if(!empty($config['base_url'])){
					$entry['base_url'] = $config['base_url'];
				}
				$builtin[] = $entry;
			}
		}

		$original_ids = array();
		foreach($builtin as $p){
			$original_ids[$p['id']] = true;
		}

		if(function_exists('apply_filters')){
			$builtin = apply_filters('softaculous_ai_providers', $builtin);
		}

		// Post-process the filter's output. We intentionally DO NOT write anything to
		// $this->data['providers'] here - the filter's api_key/base_url are loaded
		// at runtime from filter.php on every request (see ai_php_get_filter_provider_config()
		// in ai_launcher.php) and never persisted into the user's settings file
		// (which lives in their home directory and would otherwise leak the key).
		foreach($builtin as &$p){
			$pid = $p['id'];
			$is_new = !isset($original_ids[$pid]);
			
			// Auto-prefix 'custom:' to any non-built-in provider ID returned by the
			// filter, so the entry is always routed through the CustomProvider class.
			if(!empty($p['id']) && !empty($is_new) && function_exists('ai_php_normalize_filter_provider_id')){
				$pid = $p['id'] = ai_php_normalize_filter_provider_id($p['id']);
			}
			
			$auth_type = !empty($p['auth_type']) ? $p['auth_type'] : 'api_key';
			$has_predefined_key = !empty($p['api_key']);
			$is_keyless = ($auth_type === 'none');

			if($is_keyless || $has_predefined_key){
				$p['connected'] = true;
				$p['no_key'] = true;
				if($is_new){
					$p['is_managed'] = true;
				}
			}else{
				$p['connected'] = $this->is_provider_connected($pid);
			}

			unset($p['api_key']);
		}
		unset($p);

		$seen_ids = array();
		$deduped = array();
		foreach($builtin as $p){
			$id = $p['id'];
			if(isset($seen_ids[$id])){
				$deduped[$seen_ids[$id]] = $p;
			}else{
				$seen_ids[$id] = count($deduped);
				$deduped[] = $p;
			}
		}

		return $deduped;
	}

	private function get_flat_models($provider_id, $config = array()) {
		if ($provider_id === 'opencode_zen' || $provider_id === 'opencode_zen_premium') {
			$provider = new OpenCodeZenProvider($provider_id);
		} elseif ($provider_id === 'ollama_cloud') {
			$provider = new OllamaCloudProvider();
		} elseif ($provider_id === 'anthropic') {
			$provider = new AnthropicProvider();
			if (!empty($config['api_key'])) {
				$provider->set_runtime_api_key($config['api_key']);
			}
		} else {
			return array();
		}
		$models = $provider->get_supported_models();
		$flat = array();
		foreach ($models as $mid => $minfo) {
			$flat[$mid] = is_array($minfo) ? ($minfo['name'] ?? $mid) : $minfo;
		}
		return $flat;
	}

	public function get_all_models(): array{
		$providers = $this->get_all_providers();
		$models = [];
		foreach($providers as $p){
			if(!$p['connected']) continue;
			foreach($p['models'] as $mid => $mname){
				$models[] = ['id' => $mid, 'name' => is_array($mname) ? ($mname['name'] ?? $mid) : $mname, 'provider' => $p['id'], 'provider_name' => $p['name']];
			}
		}
		return $models;
	}

	public function get_favorite_models(): array{
		return $this->data['favorite_models'] ?? [];
	}

	public function add_favorite_model(string $model_id): void{
		if(!isset($this->data['favorite_models'])){
			$this->data['favorite_models'] = [];
		}
		if(!in_array($model_id, $this->data['favorite_models'])){
			$this->data['favorite_models'][] = $model_id;
			$this->save();
		}
	}

	public function remove_favorite_model(string $model_id): void{
		if(!isset($this->data['favorite_models'])){
			$this->data['favorite_models'] = [];
			return;
		}
		$this->data['favorite_models'] = array_values(array_diff($this->data['favorite_models'], [$model_id]));
		$this->save();
	}

	public function get_permissions(): array{
		return $this->data['permissions'] ?? [];
	}

	public function set_permission(string $key, string $value): void{
		if(!isset($this->data['permissions'])){
			$this->data['permissions'] = [];
		}
		$this->data['permissions'][$key] = $value;
		$this->save();
	}

	public function save(): bool{
		return AIFileHandler::write($this->file_path, $this->data);
	}

	/**
	 * Auto-detect models from models.dev cache for a given provider.
	 * Returns a model_id => model_name array of models not already in the hardcoded list.
	 */
	private function detect_models_from_dev($provider_id): array{
		if(!function_exists('ai_get_models_dev_cache')){
			return array();
		}

		$cache = ai_get_models_dev_cache();
		if(empty($cache)) return array();

		$models = array();
		foreach($cache as $key => $info){
			// Match by provider ID
			if(($info['provider'] ?? '') === $provider_id){
				$model_id = $info['model'] ?? '';
				$model_name = $info['name'] ?? $model_id;
				if($model_id && !isset($models[$model_id])){
					$models[$model_id] = $model_name;
				}
			}
		}

		return $models;
	}
}
