<?php

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

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

class AIConversation {
	private $file_path;
	private $data;

	private function __construct($file_path, array $data = array()){
		$this->file_path = $file_path;
		// Ensure title_manual exists. For backward compatibility, if the field
		// is missing but a non-empty title exists, assume it was manually set
		// (older versions did not persist title_manual). New/empty conversations
		// default to false so auto-title can kick in.
		if(!isset($data['title_manual'])){
			$data['title_manual'] = !empty($data['title']);
		}
		$this->data = $data;
	}

	public static function create($file_path, $project_path, $id = ''){
		AIFileHandler::ensure_dir(dirname($file_path));
		$file_path = preg_replace('/\.json$/', '.json.php', $file_path);

		if(empty($id)){
			$id = 'conv_' . substr(md5(uniqid(mt_rand(), true)), 0, 12);
		}

		$data = array(
			'id' => $id,
			'title' => '',
			'title_manual' => false,
			'project_path' => $project_path,
			'mode' => 'build',
			'todos' => array(),
			'last_status' => '',
			'created_at' => time(),
			'updated_at' => time(),
			'messages' => array()
		);

		$conv = new self($file_path, $data);
		$conv->save();
		return $conv;
	}

	public static function load($file_path){
		$file_path = preg_replace('/\.json$/', '.json.php', $file_path);
		$data = AIFileHandler::read($file_path);
		if($data === null) return null;
		return new self($file_path, $data);
	}

	public static function delete($file_path){
		$file_path = preg_replace('/\.json$/', '.json.php', $file_path);
		return AIFileHandler::delete($file_path);
	}

	public static function list_for_project($dir){
		AIFileHandler::ensure_dir($dir);
		$files = AIFileHandler::list_files($dir, 'conv_*.json.php');
		$sessions = array();
		foreach($files as $f){
			// Skip redo files - they're not conversations
			if(strpos($f, '.redo.') !== false) continue;
			$data = AIFileHandler::read($f);
			if(!empty($data) && !empty($data['id'])){
			$sessions[] = array(
				'id' => $data['id'],
				'title' => !empty($data['title']) ? $data['title'] : __('Untitled'),
				'mode' => isset($data['mode']) ? $data['mode'] : 'build',
				'message_count' => isset($data['messages']) ? count(array_filter($data['messages'], function($m){ return (isset($m['role']) && ($m['role'] === 'user' || $m['role'] === 'assistant')); })) : 0,
				'last_status' => !empty($data['last_status']) ? $data['last_status'] : '',
				'running' => AISession::is_conv_running($dir, $data['id']),
				'created_at' => isset($data['created_at']) ? $data['created_at'] : 0,
				'updated_at' => isset($data['updated_at']) ? $data['updated_at'] : 0
			);
			}
		}
		usort($sessions, function($a, $b){ return (isset($b['updated_at']) ? $b['updated_at'] : 0) - (isset($a['updated_at']) ? $a['updated_at'] : 0); });
		return $sessions;
	}

	public function add_user_message($content, array $attachments = array()){
		$msg = array(
			'id' => 'msg_' . substr(md5(uniqid(mt_rand(), true)), 0, 10),
			'role' => 'user',
			'content' => $content,
			'attachments' => $attachments,
			'time' => time()
		);
		$this->data['messages'][] = $msg;
		$this->data['updated_at'] = time();
		return $msg['id'];
	}

	public function add_assistant_content(array $parts, $model = '', array $usage = array(), $provider = ''){
		$msg = array(
			'id' => 'msg_' . substr(md5(uniqid(mt_rand(), true)), 0, 10),
			'role' => 'assistant',
			'parts' => $parts,
			'model' => $model,
			'provider' => $provider,
			'usage' => $usage,
			'time' => time()
		);
		$this->data['messages'][] = $msg;
		$this->data['updated_at'] = time();
		return $msg['id'];
	}

	public function add_tool_result($tool_call_id, $content, $is_error = false, $diff = ''){
		$this->data['messages'][] = array(
			'role' => 'tool_result',
			'tool_call_id' => $tool_call_id,
			'content' => $content,
			'is_error' => $is_error,
			'diff' => $diff,
			'time' => time()
		);
		$this->data['updated_at'] = time();
	}

	public function get_for_api(){
		$messages = array();

		foreach($this->data['messages'] as $msg){
			$role = isset($msg['role']) ? $msg['role'] : '';

			if($role === 'user'){
				$messages[] = array('role' => 'user', 'content' => isset($msg['content']) ? $msg['content'] : '');
			}elseif($role === 'assistant'){
				$parts = isset($msg['parts']) ? $msg['parts'] : array();
				$text_parts = array();
				$tool_calls = array();

				foreach($parts as $part){
					$part_type = isset($part['type']) ? $part['type'] : '';
					if($part_type === 'text' && !empty($part['text'])){
						$text_parts[] = $part['text'];
					}elseif($part_type === 'tool_use'){
						$tool_calls[] = $part;
					}
				}

				if(!empty($tool_calls)){
					$content = '';
					if(!empty($text_parts)) $content = implode("\n", $text_parts);

					$assistant_msg = array('role' => 'assistant');
					if(!empty($content)) $assistant_msg['content'] = $content;
					else $assistant_msg['content'] = null;

					$assistant_msg['tool_calls'] = array_map(function($tc, $i){
						return array(
							'id' => isset($tc['id']) ? $tc['id'] : 'call_'.$i,
							'type' => 'function',
							'function' => array(
								'name' => isset($tc['name']) ? $tc['name'] : '',
								'arguments' => json_encode(isset($tc['input']) && is_array($tc['input']) ? (empty($tc['input']) ? new stdClass() : $tc['input']) : new stdClass())
							)
						);
					}, $tool_calls, array_keys($tool_calls));

					$messages[] = $assistant_msg;
				}else{
					$messages[] = array('role' => 'assistant', 'content' => implode("\n", $text_parts));
				}

			}elseif($role === 'tool_result'){
				$messages[] = array(
					'role' => 'tool',
					'tool_call_id' => isset($msg['tool_call_id']) ? $msg['tool_call_id'] : '',
					'content' => isset($msg['content']) ? $msg['content'] : ''
				);
			}
		}

		return $messages;
	}

	public function get_for_api_anthropic(){
		$messages = array();

		foreach($this->data['messages'] as $msg){
			$role = isset($msg['role']) ? $msg['role'] : '';

			if($role === 'user'){
				$messages[] = array('role' => 'user', 'content' => isset($msg['content']) ? $msg['content'] : '');
			}elseif($role === 'assistant'){
				$parts = isset($msg['parts']) ? $msg['parts'] : array();
				$content = array();

				foreach($parts as $part){
					$part_type = isset($part['type']) ? $part['type'] : '';
					if($part_type === 'text' && !empty($part['text'])){
						$content[] = array('type' => 'text', 'text' => $part['text']);
					}elseif($part_type === 'tool_use'){
						$content[] = array(
							'type' => 'tool_use',
							'id' => isset($part['id']) ? $part['id'] : '',
							'name' => isset($part['name']) ? $part['name'] : '',
							'input' => isset($part['input']) ? $part['input'] : array()
						);
					}
				}

				$messages[] = array('role' => 'assistant', 'content' => $content);

			}elseif($role === 'tool_result'){
				$messages[] = array(
					'role' => 'user',
					'content' => array(array(
						'type' => 'tool_result',
						'tool_use_id' => isset($msg['tool_call_id']) ? $msg['tool_call_id'] : '',
						'content' => isset($msg['content']) ? $msg['content'] : ''
					))
				);
			}
		}

		return $messages;
	}

	public function get_for_api_google(){
		$contents = array();

		foreach($this->data['messages'] as $msg){
			$role = isset($msg['role']) ? $msg['role'] : '';

			if($role === 'user' && isset($msg['content']) && is_string($msg['content'])){
				$contents[] = array('role' => 'user', 'parts' => array(array('text' => $msg['content'])));
			}elseif($role === 'assistant'){
				$parts = isset($msg['parts']) ? $msg['parts'] : array();
				$g_parts = array();
				foreach($parts as $part){
					$part_type = isset($part['type']) ? $part['type'] : '';
					if($part_type === 'text' && !empty($part['text'])){
						$g_parts[] = array('text' => $part['text']);
					}elseif($part_type === 'tool_use'){
						$g_parts[] = array('functionCall' => array('name' => isset($part['name']) ? $part['name'] : '', 'args' => isset($part['input']) ? $part['input'] : array()));
					}
				}
				if(!empty($g_parts)){
					$contents[] = array('role' => 'model', 'parts' => $g_parts);
				}
			}elseif($role === 'tool_result'){
				$contents[] = array(
					'role' => 'function',
					'parts' => array(array('text' => isset($msg['content']) ? $msg['content'] : ''))
				);
			}
		}

		return $contents;
	}

	public function get_all(){
		return $this->data;
	}

	public function get_messages(){
		return isset($this->data['messages']) ? $this->data['messages'] : array();
	}

	public function set_messages($messages){
		$this->data['messages'] = $messages;
	}

	public function get_id(){
		return isset($this->data['id']) ? $this->data['id'] : '';
	}

	public function get_title(){
		return isset($this->data['title']) ? $this->data['title'] : '';
	}

	public function set_status($status){
		$this->data['last_status'] = $status;
	}

	public function get_status(){
		return isset($this->data['last_status']) ? $this->data['last_status'] : '';
	}

	public function is_title_manual(){
		return !empty($this->data['title_manual']);
	}

	public function set_title($title, $manual = true){
		$this->data['title'] = $title;
		if($manual){
			$this->data['title_manual'] = true;
		}
	}

	public function get_mode(){
		return isset($this->data['mode']) ? $this->data['mode'] : 'build';
	}

	public function set_mode($mode){
		$this->data['mode'] = $mode;
	}

	public function get_todos(){
		return isset($this->data['todos']) ? $this->data['todos'] : array();
	}

	public function set_todos(array $todos){
		$this->data['todos'] = $todos;
	}

	public function count_messages(){
		return isset($this->data['messages']) ? count($this->data['messages']) : 0;
	}

	public function count_user_messages(){
		$count = 0;
		$msgs = isset($this->data['messages']) ? $this->data['messages'] : array();
		foreach($msgs as $msg){
			if((isset($msg['role']) ? $msg['role'] : '') === 'user') $count++;
		}
		return $count;
	}

	public function get_token_estimate(){
		$total_text = '';
		$msgs = isset($this->data['messages']) ? $this->data['messages'] : array();
		foreach($msgs as $msg){
			if(!empty($msg['content']) && is_string($msg['content'])){
				$total_text .= $msg['content'] . ' ';
			}
			if(!empty($msg['parts']) && is_array($msg['parts'])){
				foreach($msg['parts'] as $part){
					if(!empty($part['text'])) $total_text .= $part['text'] . ' ';
					if(!empty($part['input'])) $total_text .= json_encode($part['input']) . ' ';
				}
			}
		}
		return intval(strlen($total_text) / 4);
	}

	public function compact($keep_last_n = 4){
		$msgs = $this->data['messages'];

		// Separate compact summary messages from real messages
		$real_msgs = array();
		$existing_summaries = array();
		foreach($msgs as $msg){
			if((isset($msg['id']) ? $msg['id'] : '') === 'msg_compact'){
				$existing_summaries[] = $msg;
			}else{
				$real_msgs[] = $msg;
			}
		}

		// Build the existing summary text from prior compactions
		$prior_summary = '';
		foreach($existing_summaries as $s){
			$content = isset($s['content']) ? $s['content'] : '';
			if(!empty($content)) $prior_summary .= $content . "\n";
		}

		$user_msgs = array();
		foreach($real_msgs as $i => $msg){
			if((isset($msg['role']) ? $msg['role'] : '') === 'user') $user_msgs[] = $i;
		}

		if(count($user_msgs) === 0 && empty($prior_summary)) return false;

		// Helper to build summary from messages
		$build_summary = function($msgs_to_summarize) use ($prior_summary){
			$summary_parts = array();
			$task_summary = array();
			if(!empty($prior_summary)) $summary_parts[] = trim($prior_summary);
			foreach($msgs_to_summarize as $msg){
				$role = isset($msg['role']) ? $msg['role'] : '';
				if($role === 'user' && !empty($msg['content'])){
					$summary_parts[] = 'User: ' . mb_substr($msg['content'], 0, 150);
				}elseif($role === 'assistant'){
					$parts = isset($msg['parts']) ? $msg['parts'] : array();
					foreach($parts as $part){
						if(($part['type'] ?? '') === 'tool_use'){
							$task_summary[] = 'Used ' . ($part['name'] ?? 'tool') . ': ' . mb_substr(json_encode($part['input'] ?? []), 0, 80);
						}
					}
					$text = '';
					foreach($parts as $part){
						if(($part['type'] ?? '') === 'text' && !empty($part['text'])) $text .= $part['text'];
					}
					if($text) $summary_parts[] = 'Assistant: ' . mb_substr($text, 0, 150);
				}elseif($role === 'tool_result' && !empty($msg['content'])){
					$summary_parts[] = 'Result: ' . mb_substr(is_array($msg['content']) ? json_encode($msg['content']) : $msg['content'], 0, 150);
				}
			}
			return implode("\n", array_merge($summary_parts, array_slice($task_summary, 0, 10)));
		};

		// Single real user message (or none): compact the responses + any prior summary
		if(count($user_msgs) <= 1){
			if(count($user_msgs) === 1){
				$user_idx = $user_msgs[0];
				$after_user = array_slice($real_msgs, $user_idx + 1);
				$to_summarize = array_slice($real_msgs, 0, $user_idx);
				if(empty($after_user) && empty($to_summarize) && empty($prior_summary)) return false;
				$full_summary = $build_summary(array_merge($to_summarize, $after_user));
				if(empty($full_summary)) return false;
				$this->data['messages'] = array_slice($real_msgs, $user_idx, 1);
				array_push($this->data['messages'], array(
					'id' => 'msg_compact',
					'role' => 'user',
					'content' => "[Previous conversation summary]:\n" . $full_summary,
					'time' => time()
				));
			}else{
				// No user messages at all, just prior summaries - re-merge
				if(empty($prior_summary)) return false;
				$this->data['messages'] = array(array(
					'id' => 'msg_compact',
					'role' => 'user',
					'content' => "[Previous conversation summary]:\n" . trim($prior_summary),
					'time' => time()
				));
			}
			return true;
		}

		// Multiple user messages: keep last N exchanges, compact the rest
		$effective_keep = min($keep_last_n, count($user_msgs) - 1);
		if($effective_keep < 1) $effective_keep = 1;

		$keep_from = $user_msgs[count($user_msgs) - $effective_keep];

		$removed = array_slice($real_msgs, 0, $keep_from);
		if(count($removed) === 0 && empty($prior_summary)) return false;

		$full_summary = $build_summary($removed);
		if(empty($full_summary)) return false;

		$this->data['messages'] = array_slice($real_msgs, $keep_from);
		array_unshift($this->data['messages'], array(
			'id' => 'msg_compact',
			'role' => 'user',
			'content' => "[Previous conversation summary]:\n" . $full_summary,
			'time' => time()
		));
		return true;
	}

	public function compact_with_summary($summary, $keep_last_n = 4){
		$msgs = $this->data['messages'];

		// Remove existing compact summaries - they get replaced by the new one
		$real_msgs = array();
		foreach($msgs as $msg){
			if((isset($msg['id']) ? $msg['id'] : '') === 'msg_compact') continue;
			$real_msgs[] = $msg;
		}

		$user_msgs = array();
		foreach($real_msgs as $i => $msg){
			if((isset($msg['role']) ? $msg['role'] : '') === 'user') $user_msgs[] = $i;
		}

		if(count($user_msgs) === 0) return;

		if($keep_last_n >= count($user_msgs)) $keep_last_n = max(1, count($user_msgs) - 1);

		$keep_from = $user_msgs[max(0, count($user_msgs) - $keep_last_n)];
		$this->data['messages'] = array_slice($real_msgs, $keep_from);
		array_unshift($this->data['messages'], array(
			'id' => 'msg_compact',
			'role' => 'user',
			'content' => "[Previous conversation summary]:\n" . $summary,
			'time' => time()
		));
	}

	public function get_messages_for_compaction($keep_last_n = 4){
		$msgs = $this->data['messages'];

		// Separate compact summaries from real messages
		$real_msgs = array();
		$prior_summary = '';
		foreach($msgs as $msg){
			if((isset($msg['id']) ? $msg['id'] : '') === 'msg_compact'){
				$content = isset($msg['content']) ? $msg['content'] : '';
				if(!empty($content)) $prior_summary .= $content . "\n\n";
			}else{
				$real_msgs[] = $msg;
			}
		}

		$user_msgs = array();
		foreach($real_msgs as $i => $msg){
			if((isset($msg['role']) ? $msg['role'] : '') === 'user') $user_msgs[] = $i;
		}

		if(count($user_msgs) === 0) return '';

		if($keep_last_n >= count($user_msgs)) $keep_last_n = max(1, count($user_msgs) - 1);

		$keep_from = $user_msgs[max(0, count($user_msgs) - $keep_last_n)];
		$removed = array_slice($real_msgs, 0, $keep_from);

		$text = '';
		if(!empty($prior_summary)) $text .= trim($prior_summary) . "\n\n";
		foreach($removed as $msg){
			$role = isset($msg['role']) ? $msg['role'] : '';
			if($role === 'user' && !empty($msg['content'])){
				$text .= "User: " . $msg['content'] . "\n\n";
			}elseif($role === 'assistant'){
				$parts = isset($msg['parts']) ? $msg['parts'] : array();
				foreach($parts as $part){
					if(($part['type'] ?? '') === 'text' && !empty($part['text'])){
						$text .= "Assistant: " . mb_substr($part['text'], 0, 500) . "\n\n";
					}elseif(($part['type'] ?? '') === 'tool_use'){
						$text .= "Tool call: " . ($part['name'] ?? 'tool') . "(" . mb_substr(json_encode($part['input'] ?? []), 0, 200) . ")\n\n";
					}
				}
			}elseif($role === 'tool_result'){
				$output = mb_substr($msg['content'] ?? '', 0, 300);
				$text .= "Tool result: " . $output . "\n\n";
			}
		}
		return $text;
	}

	public function clear(){
		$this->data['messages'] = array();
		$this->data['todos'] = array();
		$this->data['updated_at'] = time();
	}

	public function truncate_after_message($message_id){
		$found = false;
		$kept = array();
		foreach($this->data['messages'] as $msg){
			if($found) break;
			$kept[] = $msg;
			if(isset($msg['id']) && $msg['id'] === $message_id){
				$found = true;
			}
		}
		$this->data['messages'] = $kept;
		$this->data['updated_at'] = time();
		return $found;
	}

	public function edit_message($message_id, $new_content){
		foreach($this->data['messages'] as $i => $msg){
			if(isset($msg['id']) && $msg['id'] === $message_id){
				$this->data['messages'][$i]['content'] = $new_content;
				$this->data['updated_at'] = time();
				return true;
			}
		}
		return false;
	}

	public function get_last_assistant_message_id(){
		$messages = $this->data['messages'];
		for($i = count($messages) - 1; $i >= 0; $i--){
			if(($messages[$i]['role'] ?? '') === 'assistant'){
				return $messages[$i]['id'] ?? null;
			}
		}
		return null;
	}

	public function get_last_user_message_id(){
		$messages = $this->data['messages'];
		for($i = count($messages) - 1; $i >= 0; $i--){
			if(($messages[$i]['role'] ?? '') === 'user'){
				return $messages[$i]['id'] ?? null;
			}
		}
		return null;
	}

	public function save(){
		$this->data['updated_at'] = time();
		AIFileHandler::ensure_dir(dirname($this->file_path));
		$result = AIFileHandler::write($this->file_path, $this->data);
		if($result){
			@chmod($this->file_path, 0600);
		}
		return $result;
	}
}
