<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\Controller;
use App\Core\Response;
use App\Models\ProductModel;
use App\Models\AuditLogModel;

class ProductController extends Controller
{
    private ProductModel $productModel;
    private AuditLogModel $auditLogger;

    public function __construct()
    {
        $this->productModel = new ProductModel();
        $this->auditLogger = new AuditLogModel();
    }

    public function index(): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $filters = [
            'search' => $_GET['search'] ?? null,
            'category_id' => $_GET['category_id'] ?? null,
            'low_stock' => $_GET['low_stock'] ?? null,
            'sort' => $_GET['sort'] ?? 'p.name',
            'order' => $_GET['order'] ?? 'ASC'
        ];
        $page = (int) ($_GET['page'] ?? 1);
        $perPage = (int) ($_GET['per_page'] ?? 20);

        $products = $this->productModel->getAll($tenantId, $filters, $page, $perPage);
        $total = $this->productModel->countAll($tenantId, $filters);

        Response::paginated($products, $total, $page, $perPage);
    }

    public function lowStock(): void
    {
        $_GET['low_stock'] = 1;
        $this->index();
    }

    public function movements(string $id): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $filters = $_GET;
        $movements = $this->productModel->getStockMovements((int) $id, $tenantId, $filters);
        Response::success($movements);
    }

    public function export(): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $filters = $_GET;
        $this->productModel->exportToCSV($tenantId, $filters);
    }

    public function uploadImage(): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        if (empty($_FILES['image'])) {
            Response::error('No image provided');
        }

        $file = $_FILES['image'];
        if ($file['error'] !== UPLOAD_ERR_OK) {
            Response::error('Image upload failed with error code ' . $file['error']);
        }

        $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
        if (!in_array($file['type'], $allowedTypes)) {
            Response::error('Invalid file type');
        }

        $fileName = uniqid() . '_' . preg_replace('/[^a-zA-Z0-9.-]/', '_', $file['name']);
        $relativePath = 'uploads/tenant_' . $tenantId . '/products/' . $fileName;
        $absolutePath = UPLOAD_DIR . 'tenant_' . $tenantId . '/products/' . $fileName;

        if (!is_dir(dirname($absolutePath))) {
            mkdir(dirname($absolutePath), 0755, true);
        }

        if (move_uploaded_file($file['tmp_name'], $absolutePath)) {
            Response::success(['path' => $relativePath], 'Image uploaded');
        }

        Response::error('Failed to save image. Is directory writable? Path: ' . $absolutePath);
    }


    public function import(): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $userId = (int) $_REQUEST['user']['sub'];

        if (empty($_FILES['file']))
            Response::error('No file uploaded');

        $file = $_FILES['file']['tmp_name'];
        $result = $this->productModel->importFromCSV($file, $tenantId, $userId);
        Response::success($result, "Import completed");
    }

    public function store(): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $userId = (int) $_REQUEST['user']['sub'];
        $data = $this->getJsonBody();

        $this->validate($data, [
            'name' => 'required',
            'sale_price' => 'required|numeric',
            'tax_rate' => 'numeric'
        ]);

        // Allowed columns in products table
        $allowedColumns = [
            'tenant_id',
            'product_type',
            'name',
            'sku',
            'barcode',
            'category_id',
            'unit_id',
            'hsn_code',
            'description',
            'sale_price',
            'purchase_price',
            'mrp',
            'tax_rate',
            'tax_preference',
            'track_inventory',
            'opening_stock',
            'opening_stock_value',
            'reorder_level',
            'reorder_quantity',
            'image',
            'images',
            'is_active',
            'costing_method',
            'created_by'
        ];

        if (isset($data['images']) && is_array($data['images'])) {
            $data['image'] = !empty($data['images']) ? $data['images'][0] : null;
            $data['images'] = json_encode($data['images']);
        }

        // Filter data to only include allowed columns
        $filteredData = [];
        foreach ($allowedColumns as $col) {
            if (isset($data[$col])) {
                $filteredData[$col] = $data[$col] === '' ? null : $data[$col];
            }
        }

        $filteredData['tenant_id'] = $tenantId;
        $filteredData['created_by'] = $userId;

        try {
            $id = $this->productModel->createProduct($filteredData, $userId);
            Response::success(['id' => $id], 'Product created successfully', 201);
        } catch (\Exception $e) {
            Response::error($e->getMessage());
        }
    }

    public function search(): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $query = $_GET['q'] ?? '';
        if (strlen($query) < 2)
            Response::success([]);

        $results = $this->productModel->searchProducts($tenantId, $query);
        Response::success($results);
    }

    public function show(string $id): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $product = $this->productModel->getById((int) $id, $tenantId);
        if (!$product)
            Response::error('Product not found', [], 404);
        Response::success($product);
    }

    public function update(string $id): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $userId = (int) $_REQUEST['user']['sub'];
        $data = $this->getJsonBody();

        $old = $this->productModel->getById((int) $id, $tenantId);
        if (!$old)
            Response::error('Product not found', [], 404);

        $oldImages = [];
        if (!empty($old['images'])) {
            $decoded = is_string($old['images']) ? json_decode($old['images'], true) : $old['images'];
            if (is_array($decoded)) {
                $oldImages = $decoded;
            }
        }

        $newImagesList = null;
        if (isset($data['images']) && is_array($data['images'])) {
            $newImagesList = $data['images'];
            $data['image'] = !empty($newImagesList) ? $newImagesList[0] : null;
            $data['images'] = json_encode($data['images']);
        }

        // Filter data to only include allowed columns
        $allowedColumns = [
            'product_type',
            'name',
            'sku',
            'barcode',
            'category_id',
            'unit_id',
            'hsn_code',
            'description',
            'sale_price',
            'purchase_price',
            'mrp',
            'tax_rate',
            'tax_preference',
            'track_inventory',
            'opening_stock',
            'opening_stock_value',
            'reorder_level',
            'reorder_quantity',
            'image',
            'images',
            'is_active',
            'costing_method'
        ];

        $filteredData = [];
        foreach ($allowedColumns as $col) {
            if (isset($data[$col])) {
                $filteredData[$col] = $data[$col] === '' ? null : $data[$col];
            }
        }

        if ($this->productModel->update((int) $id, $filteredData)) {
            // Delete removed images only after successful update
            if ($newImagesList !== null) {
                $removed = array_diff($oldImages, $newImagesList);
                foreach ($removed as $imgPath) {
                    $fileName = basename($imgPath);
                    $absolutePath = UPLOAD_DIR . 'tenant_' . $tenantId . '/products/' . $fileName;
                    if (file_exists($absolutePath)) {
                        @unlink($absolutePath);
                    }
                }
            }
            $this->auditLogger->log($tenantId, $userId, 'update', 'products', (int) $id, $old, $filteredData);
            Response::success([], 'Product updated');
        }
        Response::error('Update failed');
    }

    public function destroy(string $id): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $userId = (int) $_REQUEST['user']['sub'];

        // Check if has movements
        $stmt = $this->productModel->getDb()->prepare("SELECT COUNT(*) FROM stock_movements WHERE product_id = ?");
        $stmt->execute([$id]);
        if ($stmt->fetchColumn() > 1) { // 1 is usually opening stock
            Response::error('Cannot delete product with transaction history. Deactivate it instead.');
        }

        $old = $this->productModel->getById((int) $id, $tenantId);

        if ($this->productModel->delete((int) $id)) {
            // Delete physical images
            if ($old && !empty($old['images'])) {
                $oldImages = is_string($old['images']) ? json_decode($old['images'], true) : $old['images'];
                if (is_array($oldImages)) {
                    foreach ($oldImages as $imgPath) {
                        $fileName = basename($imgPath);
                        $absolutePath = UPLOAD_DIR . 'tenant_' . $tenantId . '/products/' . $fileName;
                        if (file_exists($absolutePath)) {
                            @unlink($absolutePath);
                        }
                    }
                }
            }

            $this->auditLogger->log($tenantId, $userId, 'delete', 'products', (int) $id);
            Response::success([], 'Product deleted');
        }
        Response::error('Delete failed');
    }

    /**
     * GET /api/v1/products/{id}/barcode
     */
    public function barcode(string $id): void
    {
        $tenantId = (int) $_REQUEST['tenant_id'];
        $product = $this->productModel->getById((int) $id, $tenantId);
        if (!$product || empty($product['barcode'])) {
            Response::error('Product or Barcode not found');
        }

        try {
            $generator = new \Picqer\Barcode\BarcodeGeneratorPNG();
            $barcodeData = $generator->getBarcode($product['barcode'], $generator::TYPE_CODE_128);

            header("Content-Type: image/png");
            echo $barcodeData;
            exit;
        } catch (\Exception $e) {
            Response::error('Barcode generation failed');
        }
    }
}
