<?php
namespace boru\ai\Cli;

use boru\ai\AI;
use boru\ai\Asset\Asset;
use boru\ai\Asset\MimeTypeResolver;
use boru\ai\Conversation\ConversationReference;
use boru\cli2\CLIContext;
use boru\cli2\Contracts\CommandDescriptionInterface;
use boru\cli2\Contracts\CommandInterface;
use boru\cli2\Contracts\CommandParamsInterface;
use boru\cli2\Params\Option;

class ChatCommand implements CommandInterface, CommandDescriptionInterface, CommandParamsInterface
{
    public function name()
    {
        return 'chat';
    }

    public function description()
    {
        return 'Start or resume an interactive chat session with optional inline file attachments using @file /path/to/file';
    }

    public function commandParams()
    {
        return array(
            Option::create('p|provider|Provider override (e.g. openai)'),
            Option::create('m|model|Model override'),
            Option::create('reference|reference|Use an existing external conversation reference'),
            Option::create('conversation-id|conversation-id|Use an existing internal conversation id'),
        );
    }

    public function handle(CLIContext $ctx)
    {
        $config = $ctx->get('config');
        $provider = $ctx->get('provider');
        $model = $ctx->get('model');
        $conversationId = $ctx->get('conversation-id');
        $conversationReference = $ctx->get('reference');

        if (!$provider && $config) {
            $defaults = $config->defaults();
            if (isset($defaults['provider'])) {
                $provider = $defaults['provider'];
            }
        }
        if (!$model && $config) {
            $defaults = $config->defaults();
            if (isset($defaults['model'])) {
                $model = $defaults['model'];
            }
        }
        if (!$provider) {
            $provider = 'openai';
        }

        if (!$conversationId && !$conversationReference) {
            $conversationReference = 'cli_chat_' . date('Ymd_His');
        }

        $label = $conversationId ? ('conversation-id=' . $conversationId) : ('reference=' . $conversationReference);
        $ctx->output('Interactive chat started. Conversation: ' . $label . '. Type /exit to quit. Use @file /path/to/file to attach files.');
        $ctx->output('Provider: ' . $provider . ($model ? ' | Model: ' . $model : ''));

        while (true) {
            if (function_exists('readline')) {
                $input = readline('> ');
            } else {
                echo '> ';
                $input = fgets(STDIN);
            }

            if ($input === false) {
                $ctx->output('Exiting.');
                return 0;
            }

            $input = trim($input);
            if ($input === '') {
                continue;
            }
            if ($input === '/exit' || $input === '/quit') {
                $ctx->output('Exiting.');
                return 0;
            }

            list($message, $filePaths) = $this->parseInput($input);
            if ($message === '' && empty($filePaths)) {
                continue;
            }

            /** @var AI $ai */
            $ai = AI::provider($provider);
            if ($conversationId) {
                $reference = new ConversationReference();
                $reference->id($conversationId);
                $ai->conversationReference($reference);
            } else {
                $ai->reference($conversationReference);
            }

            if ($model) {
                $ai->model($model);
            }
            if ($message !== '') {
                $ai->addMessage($message);
            }

            foreach ($filePaths as $filePath) {
                if (!file_exists($filePath)) {
                    $ctx->output('File not found: ' . $filePath);
                    continue;
                }

                $asset = $this->createAssetFromPath($filePath);
                $ai->attachAsset($asset, 'user_data', $asset->kind() ? $asset->kind() : 'file');
            }

            $buffer = '';
            try {
                $response = $ai->onStreamDelta(function ($delta) use (&$buffer) {
                    if (!is_string($delta) || $delta === '') {
                        return;
                    }

                    $buffer .= $delta;
                    echo $delta;
                })->run();

                if ($response->error()) {
                    if ($buffer !== '') {
                        echo PHP_EOL;
                    }
                    $error = $response->error();
                    if (is_array($error) && isset($error['message'])) {
                        $ctx->output('Error: ' . $error['message']);
                    } else {
                        $ctx->output('Error: ' . json_encode($error));
                    }
                    continue;
                }

                if ($buffer === '') {
                    echo $response->text();
                }
                echo PHP_EOL;
            } catch (\Exception $e) {
                $ctx->output('Error: ' . $e->getMessage());
            }
        }
    }

    protected function parseInput($input)
    {
        $filePaths = array();
        $message = preg_replace_callback('/@file\s+("([^"]+)"|\S+)/', function ($matches) use (&$filePaths) {
            $path = isset($matches[2]) && $matches[2] !== '' ? $matches[2] : $matches[1];
            $filePaths[] = $path;
            return '';
        }, $input);

        $message = trim(preg_replace('/\s+/', ' ', $message));
        return array($message, $filePaths);
    }

    protected function createAssetFromPath($path)
    {
        $asset = new Asset();
        $asset->path($path);
        $asset->filename(basename($path));
        $asset->size(file_exists($path) ? filesize($path) : null);
        if (file_exists($path)) {
            $asset->sha256(hash_file('sha256', $path));
        }

        $mimeType = MimeTypeResolver::detect($path);
        $asset->mimeType($mimeType);

        if (strpos($mimeType, 'image/') === 0) {
            $asset->kind('image');
        } else {
            $asset->kind('file');
        }

        return $asset;
    }
}
