<?php
/**
 * Admin AJAX controller for RMBG PRO.
 *
 * Endpoints:
 * - action=removeBackground&id_image=XX
 * - action=bulkRemoveBackground&last_id=XX&limit=YY
 */

if (!defined('_PS_VERSION_')) {
    exit;
}

class AdminRmbgAjaxController extends ModuleAdminController
{
    public function __construct()
    {
        parent::__construct();
        $this->ajax = (bool) Tools::getValue('ajax');
    }

    public function initContent()
    {
        if (!$this->ajax) {
            Tools::redirectAdmin($this->context->link->getAdminLink('AdminRmbgPro', true));
        }

        parent::initContent();

        $token = (string) Tools::getValue('token');
        if ($token !== (string) Tools::getAdminTokenLite('AdminRmbgAjax')) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Invalid token',
            ]));
        }

        $action = (string) Tools::getValue('action');
        if ($action === '') {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Missing action',
            ]));
        }

        try {
            if ($action === 'removeBackground') {
                $this->ajaxProcessRemoveBackground();
            } elseif ($action === 'validateKey') {
                $this->ajaxProcessValidateKey();
            } elseif ($action === 'bulkRemoveBackgroundProducts') {
                $this->ajaxProcessBulkRemoveBackgroundProducts();
            } elseif ($action === 'bulkRemoveBackground') {
                $this->ajaxProcessBulkRemoveBackground();
            } else {
                $this->ajaxDie(json_encode([
                    'success' => false,
                    'error' => 'Unknown action',
                ]));
            }
        } catch (Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
            ]));
        }
    }

    protected function ajaxProcessBulkRemoveBackgroundProducts()
    {
        $apiKey = (string) Configuration::get(Rmbgpro::CONFIG_API_KEY, '');
        if (empty($apiKey)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'API key is not configured',
            ]));
        }

        $productIds = Tools::getValue('product_ids');
        if (is_string($productIds)) {
            $decoded = json_decode($productIds, true);
            if (is_array($decoded)) {
                $productIds = $decoded;
            }
        }

        if (!is_array($productIds) || empty($productIds)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'No products selected',
            ]));
        }

        $ids = [];
        foreach ($productIds as $v) {
            $id = (int) $v;
            if ($id > 0) {
                $ids[$id] = true;
            }
        }
        $ids = array_keys($ids);
        if (empty($ids)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'No valid product IDs',
            ]));
        }

        $ids = array_slice($ids, 0, 20);

        $outputFormat = (string) Configuration::get(Rmbgpro::CONFIG_OUTPUT_FORMAT, 'png');
        $allowedFormats = ['png', 'jpg', 'jpeg', 'webp', 'avif'];
        if (!in_array($outputFormat, $allowedFormats, true)) {
            $outputFormat = 'png';
        }

        $processed = 0;
        $failed = [];

        foreach ($ids as $idProduct) {
            try {
                $rows = Db::getInstance()->executeS(
                    'SELECT i.id_image
                     FROM ' . _DB_PREFIX_ . 'image i
                     WHERE i.id_product = ' . (int) $idProduct . '
                     ORDER BY i.position ASC, i.id_image ASC'
                );

                if (!is_array($rows) || empty($rows)) {
                    $failed[] = ['id_product' => (int) $idProduct, 'error' => 'No product images found'];
                    continue;
                }

                foreach ($rows as $row) {
                    $idImage = (int) ($row['id_image'] ?? 0);
                    if ($idImage <= 0) {
                        continue;
                    }

                    try {
                        $image = new Image($idImage);
                        if ((int) $image->id !== $idImage) {
                            $failed[] = ['id_product' => (int) $idProduct, 'id_image' => $idImage, 'error' => 'Image not found'];
                            continue;
                        }

                        $sourcePath = $this->getProductImagePath($image);
                        if (!$sourcePath || !file_exists($sourcePath)) {
                            $failed[] = ['id_product' => (int) $idProduct, 'id_image' => $idImage, 'error' => 'Source image file not found'];
                            continue;
                        }

                        $processedBinary = $this->callRmbgApi($apiKey, $sourcePath, $outputFormat);
                        $this->replaceProductImageAndRegenerateThumbnails($image, $processedBinary, $outputFormat);
                        $processed++;
                    } catch (Exception $e) {
                        $failed[] = ['id_product' => (int) $idProduct, 'id_image' => $idImage, 'error' => $e->getMessage()];
                    }
                }
            } catch (Exception $e) {
                $failed[] = ['id_product' => (int) $idProduct, 'error' => $e->getMessage()];
            }
        }

        $this->ajaxDie(json_encode([
            'success' => true,
            'processed' => $processed,
            'failed' => $failed,
        ]));
    }

    protected function ajaxProcessValidateKey()
    {
        $apiKey = (string) Configuration::get(Rmbgpro::CONFIG_API_KEY, '');
        if (empty($apiKey)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'API key is not configured',
            ]));
        }

        $testImagePath = $this->module->getLocalPath() . 'logo.png';
        if (!file_exists($testImagePath)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Test image is missing',
            ]));
        }

        $outputFormat = 'png';
        $this->callRmbgApi($apiKey, $testImagePath, $outputFormat);

        $this->ajaxDie(json_encode([
            'success' => true,
            'message' => 'API key is valid',
        ]));
    }

    protected function ajaxProcessRemoveBackground()
    {
        $apiKey = (string) Configuration::get(Rmbgpro::CONFIG_API_KEY, '');
        if (empty($apiKey)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'API key is not configured',
            ]));
        }

        $idImage = (int) Tools::getValue('id_image');
        if ($idImage <= 0) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Invalid image ID',
            ]));
        }

        $image = new Image($idImage);
        if ((int) $image->id !== $idImage) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Image not found',
            ]));
        }

        $sourcePath = $this->getProductImagePath($image);
        if (!$sourcePath || !file_exists($sourcePath)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Source image file not found',
            ]));
        }

        $outputFormat = (string) Configuration::get(Rmbgpro::CONFIG_OUTPUT_FORMAT, 'png');
        $allowedFormats = ['png', 'jpg', 'jpeg', 'webp', 'avif'];
        if (!in_array($outputFormat, $allowedFormats, true)) {
            $outputFormat = 'png';
        }

        $processedBinary = $this->callRmbgApi($apiKey, $sourcePath, $outputFormat);

        $this->replaceProductImageAndRegenerateThumbnails($image, $processedBinary, $outputFormat);

        $this->ajaxDie(json_encode([
            'success' => true,
            'message' => 'Background removed successfully',
        ]));
    }

    protected function ajaxProcessBulkRemoveBackground()
    {
        $apiKey = (string) Configuration::get(Rmbgpro::CONFIG_API_KEY, '');
        if (empty($apiKey)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'API key is not configured',
            ]));
        }

        $lastId = (int) Tools::getValue('last_id', 0);
        $limit = (int) Tools::getValue('limit', 10);
        $limit = max(1, min(50, $limit));

        $rows = Db::getInstance()->executeS(
            'SELECT i.id_image
             FROM ' . _DB_PREFIX_ . 'image i
             WHERE i.id_image > ' . (int) $lastId . '
             ORDER BY i.id_image ASC
             LIMIT ' . (int) $limit
        );

        $processed = 0;
        $failed = [];
        $newLastId = $lastId;

        $outputFormat = (string) Configuration::get(Rmbgpro::CONFIG_OUTPUT_FORMAT, 'png');
        $allowedFormats = ['png', 'jpg', 'jpeg', 'webp', 'avif'];
        if (!in_array($outputFormat, $allowedFormats, true)) {
            $outputFormat = 'png';
        }

        foreach ($rows as $row) {
            $idImage = (int) $row['id_image'];
            $newLastId = $idImage;

            try {
                $image = new Image($idImage);
                $sourcePath = $this->getProductImagePath($image);
                if (!$sourcePath || !file_exists($sourcePath)) {
                    $failed[] = ['id_image' => $idImage, 'error' => 'Source file missing'];
                    continue;
                }

                $processedBinary = $this->callRmbgApi($apiKey, $sourcePath, $outputFormat);
                $this->replaceProductImageAndRegenerateThumbnails($image, $processedBinary, $outputFormat);
                $processed++;
            } catch (Exception $e) {
                $failed[] = ['id_image' => $idImage, 'error' => $e->getMessage()];
            }
        }

        $hasMore = count($rows) === $limit;

        $this->ajaxDie(json_encode([
            'success' => true,
            'processed' => $processed,
            'failed' => $failed,
            'last_id' => $newLastId,
            'has_more' => $hasMore,
        ]));
    }

    protected function getProductImagePath(Image $image)
    {
        $basePath = _PS_PROD_IMG_DIR_ . $image->getExistingImgPath();

        if (file_exists($basePath . '.jpg')) {
            return $basePath . '.jpg';
        }
        if (file_exists($basePath . '.jpeg')) {
            return $basePath . '.jpeg';
        }
        if (file_exists($basePath . '.png')) {
            return $basePath . '.png';
        }
        if (file_exists($basePath . '.webp')) {
            return $basePath . '.webp';
        }

        return '';
    }

    protected function callRmbgApi($apiKey, $imagePath, $outputFormat)
    {
        if (!function_exists('curl_init')) {
            throw new Exception('cURL is required to call RMBG API');
        }

        $allowedTypes = ['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'tif', 'tiff', 'ico', 'svg', 'avif'];
        $ext = strtolower((string) pathinfo($imagePath, PATHINFO_EXTENSION));
        if (!in_array($ext, $allowedTypes, true)) {
            throw new Exception('Unsupported file type');
        }

        $fileSize = (int) filesize($imagePath);
        if ($fileSize <= 0 || $fileSize > 10 * 1024 * 1024) {
            throw new Exception('Image file is too large (max 10MB)');
        }

        $url = 'https://api.rmbg.pro/v1.0.1/remove_background';

        $boundary = bin2hex(random_bytes(12));
        $headers = [
            'Content-Type: multipart/form-data; boundary=' . $boundary,
            'Accept: */*',
            'X-API-KEY: ' . $apiKey,
        ];

        $mime = 'application/octet-stream';
        if (function_exists('mime_content_type')) {
            $mime = (string) mime_content_type($imagePath);
        } elseif (class_exists('finfo')) {
            $finfo = new finfo(FILEINFO_MIME_TYPE);
            $detected = $finfo->file($imagePath);
            if (is_string($detected) && $detected !== '') {
                $mime = $detected;
            }
        }
        $imageData = file_get_contents($imagePath);
        if ($imageData === false) {
            throw new Exception('Could not read image file');
        }

        $body = '--' . $boundary . "\r\n";
        $body .= 'Content-Disposition: form-data; name="image"; filename="' . basename($imagePath) . '"' . "\r\n";
        $body .= 'Content-Type: ' . $mime . "\r\n\r\n";
        $body .= $imageData . "\r\n";
        $body .= '--' . $boundary . "\r\n";
        $body .= 'Content-Disposition: form-data; name="return_type"' . "\r\n\r\n";
        $body .= 'base64' . "\r\n";
        $body .= '--' . $boundary . "\r\n";
        $body .= 'Content-Disposition: form-data; name="output_format"' . "\r\n\r\n";
        $body .= $outputFormat . "\r\n";
        $body .= '--' . $boundary . '--';

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 300);

        $response = curl_exec($ch);
        if ($response === false) {
            $err = curl_error($ch);
            curl_close($ch);
            throw new Exception('API request failed: ' . $err);
        }

        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            $decoded = json_decode($response, true);
            $errorMessage = is_array($decoded) && isset($decoded['error']) ? (string) $decoded['error'] : 'Unknown API error';
            throw new Exception('RMBG API error: ' . $errorMessage);
        }

        $decoded = json_decode($response, true);
        if (!is_array($decoded) || empty($decoded['image_base64'])) {
            throw new Exception('Invalid API response');
        }

        $binary = base64_decode((string) $decoded['image_base64'], true);
        if ($binary === false) {
            throw new Exception('Failed to decode API response');
        }

        return $binary;
    }

    protected function replaceProductImageAndRegenerateThumbnails(Image $image, $binary, $outputFormat)
    {
        $originalPath = $this->getProductImagePath($image);
        if (!$originalPath) {
            throw new Exception('Original image path not found');
        }

        $tmpDir = _PS_TMP_IMG_DIR_;
        $tmpFile = tempnam($tmpDir, 'rmbg_');
        if ($tmpFile === false) {
            throw new Exception('Failed to create temp file');
        }

        $tmpPath = $tmpFile . '.' . $outputFormat;
        rename($tmpFile, $tmpPath);

        if (file_put_contents($tmpPath, $binary) === false) {
            @unlink($tmpPath);
            throw new Exception('Failed to write processed image');
        }

        $backupPath = $originalPath . '.rmbg_bak';
        if (!copy($originalPath, $backupPath)) {
            @unlink($tmpPath);
            throw new Exception('Failed to create backup');
        }

        try {
            $this->deleteProductImageThumbnails($image);

            $targetExt = strtolower((string) pathinfo($originalPath, PATHINFO_EXTENSION));
            if ($targetExt !== 'jpg' && $targetExt !== 'jpeg') {
                $targetExt = 'jpg';
            }

            if (strtolower($outputFormat) === 'jpg' || strtolower($outputFormat) === 'jpeg') {
                if (!copy($tmpPath, $originalPath)) {
                    throw new Exception('Failed to replace original image');
                }
            } else {
                $size = @getimagesize($tmpPath);
                if (!is_array($size)) {
                    throw new Exception('Processed file is not a valid image');
                }

                if (!ImageManager::resize($tmpPath, $originalPath, (int) $size[0], (int) $size[1], $targetExt, true)) {
                    throw new Exception('Failed to convert processed image to JPG');
                }
            }

            $this->regenerateProductImageThumbnails($image, $originalPath);

            @unlink($tmpPath);
            @unlink($backupPath);
        } catch (Exception $e) {
            copy($backupPath, $originalPath);
            @unlink($tmpPath);
            @unlink($backupPath);
            throw $e;
        }
    }

    protected function deleteProductImageThumbnails(Image $image)
    {
        $basePath = _PS_PROD_IMG_DIR_ . $image->getExistingImgPath();
        $types = ImageType::getImagesTypes('products');
        foreach ($types as $type) {
            $path = $basePath . '-' . $type['name'] . '.jpg';
            if (file_exists($path)) {
                @unlink($path);
            }
        }
    }

    protected function regenerateProductImageThumbnails(Image $image, $originalPath)
    {
        $basePath = _PS_PROD_IMG_DIR_ . $image->getExistingImgPath();
        $types = ImageType::getImagesTypes('products');
        foreach ($types as $type) {
            $dest = $basePath . '-' . $type['name'] . '.jpg';
            ImageManager::resize(
                $originalPath,
                $dest,
                (int) $type['width'],
                (int) $type['height'],
                'jpg',
                true
            );
        }
    }
}
