<?php

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

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

class AISession {

	public static function get_base_dir($username){
		return AIFileHandler::get_ai_base_dir($username);
	}

	public static function get_session_key($username, $project_path){
		return $username . '_' . substr(md5($project_path), 0, 16);
	}

	public static function get_session_file($username, $project_path){
		return self::get_base_dir($username) . '/' . self::get_session_key($username, $project_path) . '.json.php';
	}

	public static function load($username, $project_path){
		$file = self::get_session_file($username, $project_path);
		return AIFileHandler::read($file);
	}

	public static function save($username, $project_path, array $data){
		AIFileHandler::get_ai_base_dir($username);
		$file = self::get_session_file($username, $project_path);
		$data['username'] = $username;
		$data['project_path'] = $project_path;
		$data['updated_at'] = time();
		if(empty($data['created_at'])) $data['created_at'] = time();
		return AIFileHandler::write($file, $data);
	}

	public static function delete($username, $project_path){
		$file = self::get_session_file($username, $project_path);
		return AIFileHandler::delete($file);
	}

	public static function get_conversations_dir($username, $project_path){
		$dir = self::get_base_dir($username) . '/' . self::get_session_key($username, $project_path) . '_conversations';
		AIFileHandler::ensure_dir($dir, true);
		return $dir;
	}

	public static function get_active_conversation_id($username, $project_path){
		$session = self::load($username, $project_path);
		if(!empty($session['active_conversation'])) return $session['active_conversation'];
		$conv_id = 'conv_' . substr(md5(uniqid(mt_rand(), true)), 0, 12);
		$session = $session ? $session : array();
		$session['active_conversation'] = $conv_id;
		self::save($username, $project_path, $session);
		return $conv_id;
	}

	public static function set_active_conversation($username, $project_path, $conv_id){
		$session = self::load($username, $project_path);
		$session = $session ? $session : array();
		$session['active_conversation'] = $conv_id;
		self::save($username, $project_path, $session);
	}

	public static function get_lock_file($username, $project_path){
		$conv_dir = self::get_conversations_dir($username, $project_path);
		$conv_id = self::get_active_conversation_id($username, $project_path);
		return $conv_dir . '/' . $conv_id . '.lock';
	}

	public static function check_lock($username, $project_path){
		$lock_file = self::get_lock_file($username, $project_path);
		if(!file_exists($lock_file)){
			return array('locked' => false);
		}
		$lock_age = time() - @filemtime($lock_file);
		$lock_pid = @file_get_contents($lock_file);
		$stale = false;
		if($lock_age > 300){
			$stale = true;
		}elseif(!empty($lock_pid) && function_exists('posix_kill')){
			if(!posix_kill(intval($lock_pid), 0)){
				$stale = true;
			}
		}elseif(!empty($lock_pid)){
			$stale = true;
		}
		if($stale){
			@unlink($lock_file);
			return array('locked' => false);
		}
		return array(
			'locked' => true,
			'pid' => $lock_pid,
			'age' => $lock_age
		);
	}

	/**
	 * Check whether a specific conversation is currently generating (i.e. has a
	 * live, non-stale lock file). Unlike check_lock() this works for any
	 * conversation id, not just the active one, so the UI can mark which
	 * sessions are "thinking" while the user views another session.
	 */
	public static function is_conv_running($conv_dir, $conv_id){
		if(empty($conv_id)){
			return false;
		}
		$lock_file = $conv_dir . '/' . $conv_id . '.lock';
		if(!file_exists($lock_file)){
			return false;
		}
		$lock_age = time() - @filemtime($lock_file);
		if($lock_age > 300){
			return false;
		}
		$lock_pid = @file_get_contents($lock_file);
		if(!empty($lock_pid) && function_exists('posix_kill')){
			if(!posix_kill(intval($lock_pid), 0)){
				return false;
			}
		}
		return true;
	}

	/**
	 * Returns all conversation IDs that are currently generating (have a
	 * non-stale .lock file) for a given project. Used by the frontend to
	 * show per-conversation "thinking" indicators independently.
	 */
	public static function get_running_conversations($username, $project_path){
		$conv_dir = self::get_conversations_dir($username, $project_path);
		$running = array();
		if(!is_dir($conv_dir)){
			return $running;
		}
		$lock_files = glob($conv_dir . '/*.lock');
		if(!is_array($lock_files)){
			return $running;
		}
		foreach($lock_files as $lock_file){
			$lock_age = time() - @filemtime($lock_file);
			if($lock_age > 300){
				@unlink($lock_file);
				continue;
			}
			$lock_pid = @file_get_contents($lock_file);
			if(!empty($lock_pid) && function_exists('posix_kill')){
				if(!posix_kill(intval($lock_pid), 0)){
					@unlink($lock_file);
					continue;
				}
			}
			$basename = basename($lock_file, '.lock');
			$running[] = $basename;
		}
		return $running;
	}

	public static function force_unlock($username, $project_path){
		$lock_file = self::get_lock_file($username, $project_path);
		$conv_dir = self::get_conversations_dir($username, $project_path);
		$conv_id = self::get_active_conversation_id($username, $project_path);
		$abort_file = $conv_dir . '/' . $conv_id . '.abort';
		if(file_exists($lock_file)){
			$lock_pid = @file_get_contents($lock_file);
			if(!empty($lock_pid) && function_exists('posix_kill')){
				$sig = defined('SIGTERM') ? SIGTERM : 15;
				@posix_kill(intval($lock_pid), $sig);
			}
			@unlink($lock_file);
		}
		if(file_exists($abort_file)){
			@unlink($abort_file);
		}
		return array('success' => true, 'message' => __('Lock cleared'));
	}
}
