<?php
namespace api\Controllers;

use api\ApiRouter;
use Domain\Attribute\AttributeRepository;
use Domain\Product\ProductRepository;

class ProductsApiController
{
    private $productRepo;
    private $attrRepo;

    public function __construct(ProductRepository $productRepo, AttributeRepository $attrRepo)
    {
        $this->productRepo = $productRepo;
        $this->attrRepo = $attrRepo;
    }

    public function list(): void
    {
        if (!ApiRouter::requireMethod('GET')) {
            return;
        }

        $filters = [
            'search' => isset($_GET['search']) ? $_GET['search'] : '',
            'status' => isset($_GET['status']) ? $_GET['status'] : '',
            'promoted' => isset($_GET['promoted']) ? $_GET['promoted'] : '',
        ];

        // Attribute filters: attribute_{id}={value_id}
        $attrFilters = [];
        foreach ($_GET as $key => $value) {
            if (strpos($key, 'attribute_') === 0) {
                $attrId = (int)substr($key, 10);
                if ($attrId > 0 && (int)$value > 0) {
                    $attrFilters[$attrId] = (int)$value;
                }
            }
        }
        if (!empty($attrFilters)) {
            $filters['attributes'] = $attrFilters;
        }

        $sort = isset($_GET['sort']) ? $_GET['sort'] : 'id';
        $sortDir = isset($_GET['sort_dir']) ? $_GET['sort_dir'] : 'DESC';
        $page = max(1, (int)(isset($_GET['page']) ? $_GET['page'] : 1));
        $perPage = max(1, min(100, (int)(isset($_GET['per_page']) ? $_GET['per_page'] : 50)));

        $result = $this->productRepo->listForApi($filters, $sort, $sortDir, $page, $perPage);

        ApiRouter::sendSuccess($result);
    }

    public function get(): void
    {
        if (!ApiRouter::requireMethod('GET')) {
            return;
        }

        $id = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
        if ($id <= 0) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
            return;
        }

        $product = $this->productRepo->findForApi($id);
        if ($product === null) {
            ApiRouter::sendError('NOT_FOUND', 'Product not found', 404);
            return;
        }

        ApiRouter::sendSuccess($product);
    }

    public function create(): void
    {
        if (!ApiRouter::requireMethod('POST')) {
            return;
        }

        $body = ApiRouter::getJsonBody();
        if ($body === null) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid JSON body', 400);
            return;
        }

        if (empty($body['languages']) || !is_array($body['languages'])) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing languages (at least one language with name is required)', 400);
            return;
        }

        $hasName = false;
        foreach ($body['languages'] as $lang) {
            if (is_array($lang) && !empty($lang['name'])) {
                $hasName = true;
                break;
            }
        }
        if (!$hasName) {
            ApiRouter::sendError('BAD_REQUEST', 'At least one language must have a name', 400);
            return;
        }

        if (!isset($body['price_brutto'])) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing price_brutto', 400);
            return;
        }

        if (!is_numeric($body['price_brutto']) || (float)$body['price_brutto'] < 0) {
            ApiRouter::sendError('BAD_REQUEST', 'price_brutto must be a non-negative number', 400);
            return;
        }

        $formData = $this->mapApiToFormData($body);

        $productId = $this->productRepo->saveProduct($formData);
        if ($productId === null) {
            ApiRouter::sendError('INTERNAL_ERROR', 'Failed to create product', 500);
            return;
        }

        http_response_code(201);
        echo json_encode([
            'status' => 'ok',
            'data' => ['id' => $productId],
        ], JSON_UNESCAPED_UNICODE);
    }

    public function update(): void
    {
        if (!ApiRouter::requireMethod('PUT')) {
            return;
        }

        $id = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
        if ($id <= 0) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
            return;
        }

        $existing = $this->productRepo->find($id);
        if ($existing === null) {
            ApiRouter::sendError('NOT_FOUND', 'Product not found', 404);
            return;
        }

        $body = ApiRouter::getJsonBody();
        if ($body === null) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid JSON body', 400);
            return;
        }

        $formData = $this->mapApiToFormData($body, $existing);
        $formData['id'] = $id;

        $this->productRepo->saveProduct($formData);

        $updated = $this->productRepo->findForApi($id);

        ApiRouter::sendSuccess($updated);
    }

    public function variants(): void
    {
        if (!ApiRouter::requireMethod('GET')) {
            return;
        }

        $id = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
        if ($id <= 0) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
            return;
        }

        $product = $this->productRepo->find($id);
        if ($product === null) {
            ApiRouter::sendError('NOT_FOUND', 'Product not found', 404);
            return;
        }

        if (!empty($product['parent_id'])) {
            ApiRouter::sendError('BAD_REQUEST', 'Cannot get variants of a variant product', 400);
            return;
        }

        $variants = $this->productRepo->findVariantsForApi($id);

        // Available attributes for this product
        $allAttributes = $this->attrRepo->listForApi();
        $usedAttrIds = [];
        foreach ($variants as $variant) {
            foreach ($variant['attributes'] as $a) {
                $usedAttrIds[(int)$a['attribute_id']] = true;
            }
        }

        $availableAttributes = [];
        foreach ($allAttributes as $attr) {
            if (isset($usedAttrIds[$attr['id']])) {
                $availableAttributes[] = $attr;
            }
        }

        ApiRouter::sendSuccess([
            'product_id' => $id,
            'available_attributes' => $availableAttributes,
            'variants' => $variants,
        ]);
    }

    public function create_variant(): void
    {
        if (!ApiRouter::requireMethod('POST')) {
            return;
        }

        $parentId = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
        if ($parentId <= 0) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
            return;
        }

        $body = ApiRouter::getJsonBody();
        if ($body === null) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid JSON body', 400);
            return;
        }

        if (empty($body['attributes']) || !is_array($body['attributes'])) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or empty attributes', 400);
            return;
        }

        $result = $this->productRepo->createVariantForApi($parentId, $body);
        if ($result === null) {
            ApiRouter::sendError('BAD_REQUEST', 'Cannot create variant: parent not found, is archived, is itself a variant, or combination already exists', 400);
            return;
        }

        $variant = $this->productRepo->findVariantForApi($result['id']);

        http_response_code(201);
        echo json_encode([
            'status' => 'ok',
            'data' => $variant !== null ? $variant : $result,
        ], JSON_UNESCAPED_UNICODE);
    }

    public function update_variant(): void
    {
        if (!ApiRouter::requireMethod('PUT')) {
            return;
        }

        $variantId = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
        if ($variantId <= 0) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
            return;
        }

        $body = ApiRouter::getJsonBody();
        if ($body === null) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid JSON body', 400);
            return;
        }

        $success = $this->productRepo->updateVariantForApi($variantId, $body);
        if (!$success) {
            ApiRouter::sendError('NOT_FOUND', 'Variant not found', 404);
            return;
        }

        $variant = $this->productRepo->findVariantForApi($variantId);
        ApiRouter::sendSuccess($variant);
    }

    public function delete_variant(): void
    {
        if (!ApiRouter::requireMethod('DELETE')) {
            return;
        }

        $variantId = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
        if ($variantId <= 0) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
            return;
        }

        $success = $this->productRepo->deleteVariantForApi($variantId);
        if (!$success) {
            ApiRouter::sendError('NOT_FOUND', 'Variant not found', 404);
            return;
        }

        ApiRouter::sendSuccess(['id' => $variantId, 'deleted' => true]);
    }

    public function upload_image(): void
    {
        if (!ApiRouter::requireMethod('POST')) {
            return;
        }

        $body = ApiRouter::getJsonBody();
        if ($body === null) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid JSON body', 400);
            return;
        }

        $productId = (int)($body['id'] ?? 0);
        if ($productId <= 0) {
            ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid product id', 400);
            return;
        }

        $product = $this->productRepo->find($productId);
        if ($product === null) {
            ApiRouter::sendError('NOT_FOUND', 'Product not found', 404);
            return;
        }

        $fileName = trim((string)($body['file_name'] ?? ''));
        $base64 = (string)($body['content_base64'] ?? '');
        if ($fileName === '' || $base64 === '') {
            ApiRouter::sendError('BAD_REQUEST', 'Missing file_name or content_base64', 400);
            return;
        }

        $binary = base64_decode($base64, true);
        if ($binary === false) {
            ApiRouter::sendError('BAD_REQUEST', 'Invalid content_base64 payload', 400);
            return;
        }

        $safeName = preg_replace('/[^a-zA-Z0-9._-]/', '_', basename($fileName));
        if ($safeName === '' || $safeName === null) {
            $safeName = 'image_' . md5((string)microtime(true)) . '.jpg';
        }

        // api.php działa z rootu projektu (nie z admin/), więc ścieżka bez ../
        $baseDir = 'upload/product_images/product_' . $productId;
        if (!is_dir($baseDir) && !mkdir($baseDir, 0775, true) && !is_dir($baseDir)) {
            ApiRouter::sendError('INTERNAL_ERROR', 'Failed to create target directory', 500);
            return;
        }

        $targetPath = $baseDir . '/' . $safeName;
        if (is_file($targetPath)) {
            $name = pathinfo($safeName, PATHINFO_FILENAME);
            $ext = pathinfo($safeName, PATHINFO_EXTENSION);
            $targetPath = $baseDir . '/' . $name . '_' . substr(md5($safeName . microtime(true)), 0, 8) . ($ext !== '' ? '.' . $ext : '');
        }

        if (file_put_contents($targetPath, $binary) === false) {
            ApiRouter::sendError('INTERNAL_ERROR', 'Failed to save image file', 500);
            return;
        }

        $src = '/upload/product_images/product_' . $productId . '/' . basename($targetPath);
        $alt = (string)($body['alt'] ?? '');
        $position = isset($body['o']) ? (int)$body['o'] : null;

        $db = $GLOBALS['mdb'] ?? null;
        if (!$db) {
            ApiRouter::sendError('INTERNAL_ERROR', 'Database not available', 500);
            return;
        }

        if ($position === null) {
            $max = $db->max('pp_shop_products_images', 'o', ['product_id' => $productId]);
            $position = (int)$max + 1;
        }

        $db->insert('pp_shop_products_images', [
            'product_id' => $productId,
            'src' => $src,
            'alt' => $alt,
            'o' => $position,
        ]);

        ApiRouter::sendSuccess([
            'src' => $src,
            'alt' => $alt,
            'o' => $position,
        ]);
    }

    /**
     * Mapuje dane z JSON API na format oczekiwany przez saveProduct().
     *
     * @param array $body Dane z JSON body
     * @param array|null $existing Istniejące dane produktu (partial update)
     * @return array Dane w formacie formularza
     */
    private function mapApiToFormData(array $body, ?array $existing = null): array
    {
        $d = [];

        // Status/promoted — saveProduct expects 'on' for checkboxes
        if (isset($body['status'])) {
            $d['status'] = $body['status'] ? 'on' : '';
        } elseif ($existing !== null) {
            $d['status'] = !empty($existing['status']) ? 'on' : '';
        }

        if (isset($body['promoted'])) {
            $d['promoted'] = $body['promoted'] ? 'on' : '';
        } elseif ($existing !== null) {
            $d['promoted'] = !empty($existing['promoted']) ? 'on' : '';
        }

        if (isset($body['stock_0_buy'])) {
            $d['stock_0_buy'] = $body['stock_0_buy'] ? 'on' : '';
        } elseif ($existing !== null) {
            $d['stock_0_buy'] = !empty($existing['stock_0_buy']) ? 'on' : '';
        }

        // Numeric fields — direct mapping
        $numericFields = [
            'price_brutto', 'price_netto', 'price_brutto_promo', 'price_netto_promo',
            'vat', 'quantity', 'weight',
        ];
        foreach ($numericFields as $field) {
            if (isset($body[$field])) {
                $d[$field] = $body[$field];
            } elseif ($existing !== null && isset($existing[$field])) {
                $d[$field] = $existing[$field];
            }
        }

        // saveProduct() traktuje float 0.00 jako "puste", ale cena 0 musi pozostać jawnie ustawiona.
        if (isset($d['price_brutto']) && is_numeric($d['price_brutto']) && (float)$d['price_brutto'] === 0.0) {
            $d['price_brutto'] = '0';
        }

        // String fields — direct mapping
        $stringFields = [
            'sku', 'ean', 'custom_label_0', 'custom_label_1', 'custom_label_2',
            'custom_label_3', 'custom_label_4', 'wp',
        ];
        foreach ($stringFields as $field) {
            if (isset($body[$field])) {
                $d[$field] = $body[$field];
            } elseif ($existing !== null && isset($existing[$field])) {
                $d[$field] = $existing[$field];
            }
        }

        // Foreign keys
        if (isset($body['set_id'])) {
            $d['set'] = $body['set_id'];
        } elseif ($existing !== null && isset($existing['set_id'])) {
            $d['set'] = $existing['set_id'];
        }

        if (isset($body['producer_id'])) {
            $d['producer_id'] = $body['producer_id'];
        } elseif ($existing !== null && isset($existing['producer_id'])) {
            $d['producer_id'] = $existing['producer_id'];
        }

        if (isset($body['product_unit_id'])) {
            $d['product_unit'] = $body['product_unit_id'];
        } elseif ($existing !== null && isset($existing['product_unit_id'])) {
            $d['product_unit'] = $existing['product_unit_id'];
        }

        // Languages: body.languages.pl.name → d['name']['pl']
        if (isset($body['languages']) && is_array($body['languages'])) {
            $langFields = [
                'name', 'short_description', 'description', 'meta_description',
                'meta_keywords', 'meta_title', 'seo_link', 'copy_from',
                'warehouse_message_zero', 'warehouse_message_nonzero',
                'tab_name_1', 'tab_description_1', 'tab_name_2', 'tab_description_2',
                'canonical', 'security_information',
            ];

            foreach ($body['languages'] as $langId => $langData) {
                if (!is_array($langData)) {
                    continue;
                }
                foreach ($langFields as $field) {
                    if (isset($langData[$field])) {
                        $d[$field][$langId] = $langData[$field];
                    }
                }
            }
        }

        // Categories
        if (isset($body['categories']) && is_array($body['categories'])) {
            $d['categories'] = $body['categories'];
        }

        // Related products
        if (isset($body['products_related']) && is_array($body['products_related'])) {
            $d['products_related'] = $body['products_related'];
        }

        // Custom fields (Dodatkowe pola)
        if (isset($body['custom_fields']) && is_array($body['custom_fields'])) {
            $d['custom_field_name']     = [];
            $d['custom_field_type']     = [];
            $d['custom_field_required'] = [];
            foreach ($body['custom_fields'] as $cf) {
                if (!is_array($cf) || empty($cf['name'])) {
                    continue;
                }
                $d['custom_field_name'][]     = (string)$cf['name'];
                $d['custom_field_type'][]     = !empty($cf['type']) ? (string)$cf['type'] : 'text';
                $d['custom_field_required'][] = !empty($cf['is_required']) ? 1 : 0;
            }
        }

        return $d;
    }
}
