<?php
namespace boru\ai\Provider\OpenAI;

use boru\ai\Asset\Asset;
use boru\ai\Asset\AssetProviderInterface;
use boru\ai\Asset\ProviderAssetBinding;

class OpenAIAssetProvider implements AssetProviderInterface
{
    /** @var OpenAIClient */
    protected $client;

    public function __construct(OpenAIClient $client = null)
    {
        $this->client = $client ? $client : new OpenAIClient();
    }

    public function providerName()
    {
        return 'openai';
    }

    public function client(OpenAIClient $client = null)
    {
        if ($client !== null) {
            $this->client = $client;
            return $this;
        }
        return $this->client;
    }

    public function ensureBinding(Asset $asset, $purpose = null, ProviderAssetBinding $existingBinding = null)
    {
        if ($existingBinding instanceof ProviderAssetBinding && $existingBinding->providerAssetId()) {
            return $existingBinding;
        }

        if (!$asset->path()) {
            throw new \InvalidArgumentException('OpenAI asset upload requires a local asset path');
        }

        $prepared = $this->prepareUploadAsset($asset);
        $purpose = $purpose ? $purpose : 'user_data';
        $result = $this->client()->uploadFile($prepared['path'], $purpose, array(
            'filename' => $prepared['filename'],
            'mime_type' => $prepared['mime_type'],
        ));

        $binding = $existingBinding ? $existingBinding : new ProviderAssetBinding();
        $binding->assetId($asset->id());
        $binding->provider($this->providerName());
        $binding->purpose($purpose);
        $binding->status('ready');
        $binding->mimeType($prepared['mime_type']);
        $binding->raw($result);
        $binding->createdAt(date('c'));
        $binding->updatedAt(date('c'));

        if (!empty($prepared['metadata'])) {
            foreach ($prepared['metadata'] as $key => $value) {
                $binding->metadata($key, $value);
            }
        }

        if (isset($result['id'])) {
            $binding->providerAssetId($result['id']);
        }
        if (isset($result['url'])) {
            $binding->providerUri($result['url']);
        }

        return $binding;
    }

    protected function prepareUploadAsset(Asset $asset)
    {
        $path = $asset->path();
        $filename = $asset->filename() ? $asset->filename() : basename($path);
        $mimeType = $asset->mimeType() ? $asset->mimeType() : 'application/octet-stream';
        $metadata = array();

        if ($this->requiresTextNormalization($filename, $mimeType)) {
            $normalizedPath = $this->createNormalizedTextCopy($path, $filename);
            $metadata['source_path'] = $path;
            $metadata['source_filename'] = $filename;
            $metadata['normalized_filename'] = basename($normalizedPath);
            $metadata['normalized_from_extension'] = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
            $metadata['normalized_mime_type'] = 'text/plain';

            return array(
                'path' => $normalizedPath,
                'filename' => basename($normalizedPath),
                'mime_type' => 'text/plain',
                'metadata' => $metadata,
            );
        }

        return array(
            'path' => $path,
            'filename' => $filename,
            'mime_type' => $mimeType,
            'metadata' => $metadata,
        );
    }

    protected function requiresTextNormalization($filename, $mimeType)
    {
        $extension = strtolower(pathinfo((string)$filename, PATHINFO_EXTENSION));
        if ($extension === '') {
            return false;
        }

        $supported = array(
            'bat', 'c', 'css', 'csv', 'doc', 'dot', 'eml', 'h', 'htm', 'html', 'hwp', 'hwpx',
            'js', 'json', 'ksh', 'markdown', 'md', 'mht', 'mhtml', 'mjs', 'nws', 'pdf', 'pl',
            'pot', 'ppa', 'pps', 'ppt', 'pwz', 'py', 'rst', 'srt', 'svg', 'txt', 'vcf', 'vtt',
            'wiz', 'xlb', 'xls', 'xml',
        );

        if (in_array($extension, $supported, true)) {
            return false;
        }

        if (strpos((string)$mimeType, 'text/') === 0) {
            return true;
        }

        $textLikeExtensions = array(
            'php', 'phtml', 'php3', 'php4', 'php5', 'php7', 'php8', 'inc', 'module',
            'sh', 'bash', 'zsh', 'ini', 'conf', 'yml', 'yaml', 'sql', 'log', 'env',
            'ts', 'tsx', 'jsx', 'java', 'rb', 'go', 'rs', 'cpp', 'cc', 'cxx', 'hpp',
        );

        return in_array($extension, $textLikeExtensions, true);
    }

    protected function createNormalizedTextCopy($path, $filename)
    {
        $base = pathinfo((string)$filename, PATHINFO_FILENAME);
        if ($base === '') {
            $base = 'asset';
        }

        $tmp = tempnam(sys_get_temp_dir(), 'openai_asset_');
        if ($tmp === false) {
            throw new \RuntimeException('Unable to create temporary file for OpenAI asset normalization');
        }

        $normalizedPath = $tmp . '_' . $base . '.txt';
        if (!@rename($tmp, $normalizedPath)) {
            @unlink($tmp);
            throw new \RuntimeException('Unable to prepare normalized text file for OpenAI asset upload');
        }

        $contents = file_get_contents($path);
        if ($contents === false) {
            @unlink($normalizedPath);
            throw new \RuntimeException('Unable to read asset for OpenAI normalization: ' . $path);
        }

        if (file_put_contents($normalizedPath, $contents) === false) {
            @unlink($normalizedPath);
            throw new \RuntimeException('Unable to write normalized OpenAI asset file: ' . $normalizedPath);
        }

        return $normalizedPath;
    }
}
