<?php

namespace App\Services;

use App\Models\AttributeValue;
use App\Models\Category;
use App\Models\Product;
use App\Models\StockMovement;
use App\Models\Unit;
use App\Models\VariantStockMovement;
use Illuminate\Support\Facades\DB;

class ProductImportService
{
    public function __construct(
        protected ImageService $imageService,
        protected CacheService $cacheService
    ) {}

    /**
     * Run product import from a stored CSV path. Returns report array.
     *
     * @param string $path Full path to CSV file (e.g. storage_path('app/...'))
     * @param int|null $userId User ID for stock movement created_by
     * @return array{imported: int, skipped: int, variants_imported: int, errors: array}
     */
    public function run(string $path, ?int $userId = null): array
    {
        @ini_set('memory_limit', '512M');
        $userId = $userId ?? 1;

        $data = [];
        if (($handle = fopen($path, 'r')) !== false) {
            $firstLine = fgets($handle);
            if (substr($firstLine, 0, 3) === "\xEF\xBB\xBF") {
                $firstLine = substr($firstLine, 3);
            }
            rewind($handle);
            fseek($handle, strlen($firstLine) === strlen(fgets($handle)) ? 0 : 3);
            while (($row = fgetcsv($handle, 1000, ',')) !== false) {
                $data[] = $row;
            }
            fclose($handle);
        }

        $header = array_shift($data);
        if ($header === null || !is_array($header)) {
            return ['imported' => 0, 'skipped' => 0, 'variants_imported' => 0, 'errors' => ['CSV file is empty or invalid.']];
        }

        $header = array_map(fn($v) => trim(str_replace("\xEF\xBB\xBF", '', (string) $v)), $header);

        $expectedColumns = [
            'product_type', 'name', 'category_name', 'tags', 'sku', 'price', 'mrp', 'purchase_price', 'wholesale_price',
            'opening_stock', 'barcode', 'stock', 'unit_name', 'short_desc', 'long_desc', 'is_active', 'is_featured', 'is_prebooking', 'image_name',
            'variant_attributes', 'variant_sku', 'variant_price', 'variant_mrp', 'variant_purchase_price', 'variant_wholesale_price',
            'variant_opening_stock', 'variant_barcode', 'variant_image_name',
        ];
        $columnMap = [];
        foreach ($expectedColumns as $col) {
            $index = array_search(strtolower($col), array_map('strtolower', $header));
            if ($index !== false) {
                $columnMap[$col] = $index;
            }
        }

        if (!isset($columnMap['name']) || !isset($columnMap['category_name'])) {
            return ['imported' => 0, 'skipped' => 0, 'variants_imported' => 0, 'errors' => ['CSV missing required columns: name and category_name.']];
        }
        if (!isset($columnMap['product_type'])) {
            return ['imported' => 0, 'skipped' => 0, 'variants_imported' => 0, 'errors' => ['CSV missing required column: product_type.']];
        }

        $imported = 0;
        $variantsImported = 0;
        $skipped = 0;
        $errors = [];

        $variantGroups = [];
        $singleServiceRows = [];

        foreach ($data as $index => $row) {
            $row = array_map('trim', $row);
            if (empty(array_filter($row))) {
                continue;
            }
            $productType = strtolower($row[$columnMap['product_type']] ?? 'single');
            $name = $row[$columnMap['name']] ?? '';
            if (empty($name)) {
                $skipped++;
                $errors[] = "Row " . ($index + 2) . ": Missing product name";
                continue;
            }
            if (!in_array($productType, ['single', 'variant'])) {
                $skipped++;
                $errors[] = "Row " . ($index + 2) . ": Invalid product_type '{$productType}'";
                continue;
            }
            if ($productType === 'variant') {
                $nameKey = strtolower($name);
                if (!isset($variantGroups[$nameKey])) {
                    $variantGroups[$nameKey] = [];
                }
                $variantGroups[$nameKey][] = ['row' => $row, 'index' => $index];
            } else {
                $singleServiceRows[] = ['row' => $row, 'index' => $index, 'type' => $productType];
            }
        }

        DB::beginTransaction();
        try {
            Product::withoutEvents(function () use ($singleServiceRows, $variantGroups, $columnMap, $userId, &$imported, &$skipped, &$errors, &$variantsImported) {
                foreach ($singleServiceRows as $item) {
                    $result = $this->importSingleOrServiceProduct($item['row'], $columnMap, $item['index'], $item['type'], $userId);
                    if ($result['success']) {
                        $imported++;
                    } else {
                        $skipped++;
                        $errors[] = $result['error'];
                    }
                }
                foreach ($variantGroups as $variantRows) {
                    $result = $this->importVariantProduct($variantRows, $columnMap, $userId);
                    if ($result['success']) {
                        $imported++;
                        $variantsImported += $result['variants_created'];
                    } else {
                        $skipped += count($variantRows);
                        $errors[] = $result['error'];
                    }
                }
            });
            DB::commit();
            $this->cacheService->clearProductCache();
        } catch (\Throwable $e) {
            DB::rollBack();
            $errors[] = $e->getMessage();
        }

        return [
            'imported' => $imported,
            'skipped' => $skipped,
            'variants_imported' => $variantsImported,
            'errors' => $errors,
        ];
    }

    private function importSingleOrServiceProduct(array $row, array $columnMap, int $index, string $productType, int $userId): array
    {
        try {
            $name = $row[$columnMap['name']] ?? '';
            $categoryName = $row[$columnMap['category_name']] ?? '';
            $tags = $row[$columnMap['tags']] ?? '';
            $price = $row[$columnMap['price']] ?? '';
            $mrp = $row[$columnMap['mrp']] ?? '';
            $purchase_price = $row[$columnMap['purchase_price']] ?? null;
            $wholesale_price = $row[$columnMap['wholesale_price']] ?? null;
            $openingStock = $row[$columnMap['opening_stock'] ?? $columnMap['stock'] ?? null] ?? 0;
            $barcode = $row[$columnMap['barcode']] ?? '';
            $unitName = $row[$columnMap['unit_name']] ?? '';
            $shortDesc = $row[$columnMap['short_desc']] ?? '';
            $longDesc = $row[$columnMap['long_desc']] ?? '';
            $isActive = $row[$columnMap['is_active']] ?? '1';
            $isFeatured = $row[$columnMap['is_featured']] ?? '0';
            $isPrebooking = $row[$columnMap['is_prebooking']] ?? '0';
            $imageName = $row[$columnMap['image_name']] ?? '';

            if (empty($name) || empty($categoryName)) {
                return ['success' => false, 'error' => "Row " . ($index + 2) . ": Missing required fields (name or category_name)"];
            }
            if (empty($price)) {
                return ['success' => false, 'error' => "Row " . ($index + 2) . ": Missing required field (price)"];
            }

            $category = Category::where('name', $categoryName)->first();
            if (!$category) {
                return ['success' => false, 'error' => "Row " . ($index + 2) . ": Category '{$categoryName}' not found"];
            }

            $unitId = null;
            if (!empty($unitName)) {
                $unit = Unit::where('name', $unitName)->first();
                if ($unit) {
                    $unitId = $unit->id;
                }
            }
            if (!empty($price) && (!is_numeric($price) || $price < 0)) {
                return ['success' => false, 'error' => "Row " . ($index + 2) . ": Invalid price '{$price}'"];
            }
            if (!empty($mrp) && (!is_numeric($mrp) || $mrp < 0)) {
                return ['success' => false, 'error' => "Row " . ($index + 2) . ": Invalid MRP '{$mrp}'"];
            }
            $openingStock = (int) $openingStock;
            if ($openingStock < 0) {
                $openingStock = 0;
            }
            if (!empty($barcode) && Product::where('barcode', $barcode)->exists()) {
                $barcode = '';
            }
            if (empty($barcode)) {
                $barcode = $this->generateBarcode(Product::class);
            }
            $isActive = in_array(strtolower($isActive), ['1', 'true', 'yes', 'active']);
            $isFeatured = in_array(strtolower($isFeatured), ['1', 'true', 'yes', 'featured']);
            $isPrebooking = in_array(strtolower($isPrebooking), ['1', 'true', 'yes', 'prebooking']);

            $slug = \Illuminate\Support\Str::slug($name);
            $originalSlug = $slug;
            $counter = 1;
            while (Product::where('slug', $slug)->exists()) {
                $slug = $originalSlug . '-' . $counter;
                $counter++;
            }
            $sku = isset($columnMap['sku']) && !empty(trim($row[$columnMap['sku']] ?? '')) ? trim($row[$columnMap['sku']]) : '';
            if (!empty($sku) && Product::where('sku', $sku)->exists()) {
                $sku = '';
            }
            if (empty($sku)) {
                do {
                    $sku = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', $name), 0, 10)) . '-' . time() . '-' . rand(100, 999);
                } while (Product::where('sku', $sku)->exists());
            }

            $productData = [
                'category_id' => $category->id,
                'unit_id' => $unitId,
                'product_type' => $productType,
                'parent_id' => null,
                'name' => $name,
                'slug' => $slug,
                'sku' => $sku,
                'tags' => $tags,
                'price' => $price,
                'mrp' => !empty($mrp) ? $mrp : null,
                'purchase_price' => $purchase_price,
                'wholesale_price' => $wholesale_price,
                'opening_stock' => $openingStock,
                'barcode' => $barcode,
                'current_stock' => $openingStock,
                'short_desc' => $shortDesc,
                'long_desc' => $longDesc,
                'image_path' => null,
                'image' => null,
                'is_active' => $isActive,
                'is_featured' => $isFeatured,
                'is_prebooking' => $isPrebooking,
            ];
            $product = Product::create($productData);

            if (!empty($imageName)) {
                $imageNames = array_map('trim', explode(',', $imageName));
                $firstImageName = $imageNames[0];
                $importImagePath = public_path('uploads/imports/products/' . $firstImageName);
                $allowedExtensions = ['jpg', 'jpeg', 'png', 'webp'];
                $maxSizeBytes = 5 * 1024 * 1024;
                if (file_exists($importImagePath)) {
                    $ext = strtolower(pathinfo($firstImageName, PATHINFO_EXTENSION));
                    $size = filesize($importImagePath);
                    if (in_array($ext, $allowedExtensions) && $size > 0 && $size <= $maxSizeBytes) {
                        $mimeType = mime_content_type($importImagePath) ?: 'image/jpeg';
                        $uploadedFile = new \Illuminate\Http\UploadedFile($importImagePath, $firstImageName, $mimeType, 0, true);
                        $imageResult = $this->imageService->processProductImageVariants($uploadedFile, $product->id);
                        $product->update(['image_path' => $imageResult['original'], 'image' => $imageResult]);
                    }
                }
            }
            if ($productType === 'single' && $openingStock > 0) {
                StockMovement::create([
                    'product_id' => $product->id,
                    'type' => StockMovement::TYPE_OPENING_STOCK,
                    'quantity' => $openingStock,
                    'notes' => 'Opening stock',
                    'created_by' => $userId,
                ]);
                $product->updateCurrentStock();
            }
            return ['success' => true];
        } catch (\Exception $e) {
            return ['success' => false, 'error' => "Row " . ($index + 2) . ": " . $e->getMessage()];
        }
    }

    private function importVariantProduct(array $variantRows, array $columnMap, int $userId): array
    {
        try {
            if (empty($variantRows)) {
                return ['success' => false, 'error' => 'No variant rows provided'];
            }
            $firstRow = $variantRows[0]['row'];
            $firstIndex = $variantRows[0]['index'];
            $name = $firstRow[$columnMap['name']] ?? '';
            $categoryName = $firstRow[$columnMap['category_name']] ?? '';
            $tags = $firstRow[$columnMap['tags']] ?? '';
            $price = $firstRow[$columnMap['price']] ?? '0';
            $mrp = $firstRow[$columnMap['mrp']] ?? '';
            $purchase_price = $firstRow[$columnMap['purchase_price']] ?? '0';
            $wholesale_price = $firstRow[$columnMap['wholesale_price']] ?? '0';
            $barcode = $firstRow[$columnMap['barcode']] ?? '';
            $unitName = $firstRow[$columnMap['unit_name']] ?? '';
            $shortDesc = $firstRow[$columnMap['short_desc']] ?? '';
            $longDesc = $firstRow[$columnMap['long_desc']] ?? '';
            $isActive = $firstRow[$columnMap['is_active']] ?? '1';
            $isFeatured = $firstRow[$columnMap['is_featured']] ?? '0';
            $isPrebooking = $firstRow[$columnMap['is_prebooking']] ?? '0';
            $imageName = $firstRow[$columnMap['image_name']] ?? '';

            if (empty($name) || empty($categoryName)) {
                return ['success' => false, 'error' => "Row " . ($firstIndex + 2) . ": Missing required fields"];
            }
            $category = Category::where('name', $categoryName)->first();
            if (!$category) {
                return ['success' => false, 'error' => "Row " . ($firstIndex + 2) . ": Category '{$categoryName}' not found"];
            }
            $unitId = null;
            if (!empty($unitName)) {
                $unit = Unit::where('name', $unitName)->first();
                if ($unit) {
                    $unitId = $unit->id;
                }
            }
            if (!empty($barcode) && Product::where('barcode', $barcode)->exists()) {
                $barcode = '';
            }
            if (empty($barcode)) {
                $barcode = $this->generateBarcode(Product::class);
            }
            $isActive = in_array(strtolower($isActive), ['1', 'true', 'yes', 'active']);
            $isFeatured = in_array(strtolower($isFeatured), ['1', 'true', 'yes', 'featured']);
            $isPrebooking = in_array(strtolower($isPrebooking), ['1', 'true', 'yes', 'prebooking']);
            $slug = \Illuminate\Support\Str::slug($name);
            $originalSlug = $slug;
            $counter = 1;
            while (Product::where('slug', $slug)->exists()) {
                $slug = $originalSlug . '-' . $counter;
                $counter++;
            }
            $sku = isset($columnMap['sku']) && !empty(trim($firstRow[$columnMap['sku']] ?? '')) ? trim($firstRow[$columnMap['sku']]) : '';
            if (!empty($sku) && Product::where('sku', $sku)->exists()) {
                $sku = '';
            }
            if (empty($sku)) {
                do {
                    $sku = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', $name), 0, 10)) . '-PARENT-' . time();
                } while (Product::where('sku', $sku)->exists());
            }
            $productData = [
                'category_id' => $category->id,
                'unit_id' => $unitId,
                'product_type' => 'variant',
                'parent_id' => null,
                'name' => $name,
                'slug' => $slug,
                'sku' => $sku,
                'tags' => $tags,
                'price' => !empty($price) ? $price : 0,
                'mrp' => !empty($mrp) ? $mrp : null,
                'purchase_price' => !empty($purchase_price) ? $purchase_price : 0,
                'wholesale_price' => !empty($wholesale_price) ? $wholesale_price : 0,
                'opening_stock' => 0,
                'current_stock' => 0,
                'barcode' => $barcode,
                'short_desc' => $shortDesc,
                'long_desc' => $longDesc,
                'image_path' => null,
                'image' => null,
                'is_active' => $isActive,
                'is_featured' => $isFeatured,
                'is_prebooking' => $isPrebooking,
            ];
            $product = Product::create($productData);

            if (!empty($imageName)) {
                $imageNames = array_map('trim', explode(',', $imageName));
                $firstImageName = $imageNames[0];
                $importImagePath = public_path('uploads/imports/products/' . $firstImageName);
                $allowedExtensions = ['jpg', 'jpeg', 'png', 'webp'];
                $maxSizeBytes = 5 * 1024 * 1024;
                if (file_exists($importImagePath)) {
                    $ext = strtolower(pathinfo($firstImageName, PATHINFO_EXTENSION));
                    $size = filesize($importImagePath);
                    if (in_array($ext, $allowedExtensions) && $size > 0 && $size <= $maxSizeBytes) {
                        $mimeType = mime_content_type($importImagePath) ?: 'image/jpeg';
                        $uploadedFile = new \Illuminate\Http\UploadedFile($importImagePath, $firstImageName, $mimeType, 0, true);
                        $imageResult = $this->imageService->processProductImageVariants($uploadedFile, $product->id);
                        $product->update(['image_path' => $imageResult['original'], 'image' => $imageResult]);
                    }
                }
            }

            $variantsCreated = 0;
            foreach ($variantRows as $variantItem) {
                $row = $variantItem['row'];
                $index = $variantItem['index'];
                $variantAttributes = $row[$columnMap['variant_attributes']] ?? '';
                $variantSku = $row[$columnMap['variant_sku']] ?? '';
                $variantPrice = $row[$columnMap['variant_price']] ?? '';
                $variantMrp = $row[$columnMap['variant_mrp']] ?? '';
                $variantPurchasePrice = $row[$columnMap['variant_purchase_price']] ?? '';
                $variantWholesalePrice = $row[$columnMap['variant_wholesale_price']] ?? '';
                $variantOpeningStock = (int) ($row[$columnMap['variant_opening_stock']] ?? 0);
                $variantBarcode = $row[$columnMap['variant_barcode']] ?? '';
                $variantImageName = $row[$columnMap['variant_image_name']] ?? '';

                if (empty($variantAttributes)) {
                    continue;
                }
                $attributeValueIds = $this->matchAttributeValues($variantAttributes);
                if (empty($attributeValueIds)) {
                    continue;
                }
                if (empty(trim($variantSku))) {
                    do {
                        $variantSku = 'VAR-' . $product->id . '-' . time() . '-' . rand(1000, 9999);
                    } while (Product::where('sku', $variantSku)->exists());
                }
                $finalVariantPrice = !empty($variantPrice) && is_numeric($variantPrice) ? $variantPrice : $product->price;
                if ($finalVariantPrice < 0) {
                    $finalVariantPrice = 0;
                }
                $finalVariantMrp = (!empty($variantMrp) && is_numeric($variantMrp) && $variantMrp >= 0) ? $variantMrp : $product->mrp;
                $finalvariantPurchasePrice = (!empty($variantPurchasePrice) && is_numeric($variantPurchasePrice) && $variantPurchasePrice >= 0) ? $variantPurchasePrice : $product->purchase_price;
                $finalvariantWholesalePrice = (!empty($variantWholesalePrice) && is_numeric($variantWholesalePrice) && $variantWholesalePrice >= 0) ? $variantWholesalePrice : $product->wholesale_price;
                if (!empty($variantBarcode) && Product::where('barcode', $variantBarcode)->exists()) {
                    $variantBarcode = '';
                }
                if (empty($variantBarcode)) {
                    $variantBarcode = $this->generateBarcode(Product::class);
                }
                if ($variantOpeningStock < 0) {
                    $variantOpeningStock = 0;
                }
                $attributeValues = AttributeValue::whereIn('id', $attributeValueIds)->with('attribute')->get();
                $variationName = $attributeValues->map(fn($av) => $av->attribute->name . ': ' . $av->value)->implode(' - ');
                $slug = \Illuminate\Support\Str::slug($product->name . ' ' . $variationName);
                $originalSlug = $slug;
                $counter = 1;
                while (Product::where('slug', $slug)->exists()) {
                    $slug = $originalSlug . '-' . $counter;
                    $counter++;
                }
                $variantData = [
                    'parent_id' => $product->id,
                    'product_type' => 'variant',
                    'category_id' => $product->category_id,
                    'unit_id' => $product->unit_id,
                    'tax_id' => $product->tax_id,
                    'tax_rate_snapshot' => $product->tax_rate_snapshot,
                    'brand_id' => $product->brand_id,
                    'name' => $product->name . ' - ' . $variationName,
                    'slug' => $slug,
                    'sku' => $variantSku,
                    'price' => $finalVariantPrice,
                    'mrp' => $finalVariantMrp,
                    'purchase_price' => $finalvariantPurchasePrice,
                    'wholesale_price' => $finalvariantWholesalePrice,
                    'opening_stock' => $variantOpeningStock,
                    'current_stock' => $variantOpeningStock,
                    'barcode' => $variantBarcode ?: null,
                    'hsn_code' => $product->hsn_code,
                    'image_path' => null,
                    'image' => null,
                    'short_desc' => $product->short_desc,
                    'long_desc' => $product->long_desc,
                    'is_active' => true,
                    'is_featured' => false,
                    'is_prebooking' => $product->is_prebooking,
                ];
                $variant = Product::create($variantData);

                $variantImageProcessed = false;
                if (!empty($variantImageName)) {
                    $importImagePath = public_path('uploads/imports/products/' . $variantImageName);
                    $allowedExtensions = ['jpg', 'jpeg', 'png', 'webp'];
                    $maxSizeBytes = 5 * 1024 * 1024;
                    if (file_exists($importImagePath)) {
                        $ext = strtolower(pathinfo($variantImageName, PATHINFO_EXTENSION));
                        $size = filesize($importImagePath);
                        if (in_array($ext, $allowedExtensions) && $size > 0 && $size <= $maxSizeBytes) {
                            $mimeType = mime_content_type($importImagePath) ?: 'image/jpeg';
                            $uploadedFile = new \Illuminate\Http\UploadedFile($importImagePath, $variantImageName, $mimeType, 0, true);
                            $imageResult = $this->imageService->processProductImageVariants($uploadedFile, $variant->id);
                            $variant->update(['image_path' => $imageResult['original'], 'image' => $imageResult]);
                            $variantImageProcessed = true;
                        }
                    }
                }
                if (!$variantImageProcessed) {
                    $variant->update(['image_path' => $product->image_path, 'image' => $product->image]);
                }
                $variant->attributeValues()->attach($attributeValueIds);
                if ($variantOpeningStock > 0) {
                    VariantStockMovement::create([
                        'product_id' => $variant->id,
                        'type' => VariantStockMovement::TYPE_OPENING_STOCK,
                        'quantity' => $variantOpeningStock,
                        'notes' => 'Opening stock',
                        'created_by' => $userId,
                    ]);
                    $variant->updateCurrentStock();
                }
                $variantsCreated++;
            }

            if ($variantsCreated === 0) {
                $product->delete();
                return ['success' => false, 'error' => "Product '{$name}': No valid variants created."];
            }
            return ['success' => true, 'variants_created' => $variantsCreated];
        } catch (\Exception $e) {
            return ['success' => false, 'error' => "Row " . ($firstIndex + 2) . ": " . $e->getMessage()];
        }
    }

    private function matchAttributeValues(string $variantAttributes): array
    {
        if (empty($variantAttributes)) {
            return [];
        }
        $values = array_map('trim', explode(',', $variantAttributes));
        $ids = [];
        foreach ($values as $value) {
            if (empty($value)) {
                continue;
            }
            $av = AttributeValue::where('status', 'active')->whereRaw('LOWER(value) = ?', [strtolower($value)])->first();
            if ($av) {
                $ids[] = $av->id;
            }
        }
        return $ids;
    }

    private function generateBarcode(string $modelClass, int $length = 8, string $column = 'barcode'): string
    {
        do {
            $barcode = str_pad(random_int(0, (10 ** $length) - 1), $length, '0', STR_PAD_LEFT);
        } while ($modelClass::where($column, $barcode)->exists());
        return $barcode;
    }
}
