<?php

namespace App\Modules\Inventory\Imports;

use App\Modules\Inventory\Models\Category;
use App\Modules\Inventory\Models\Product;
use App\Modules\Inventory\Models\Unit;
use Illuminate\Validation\Rule;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;

/**
 * 商品匯入主類別
 *
 * 實作 WithMultipleSheets 以限定只讀取第一個工作表（資料頁），
 * 跳過第二個工作表（填寫說明頁），避免說明頁的資料被誤匯入並觸發驗證錯誤。
 */
class ProductImport implements WithMultipleSheets
{
    public function __construct()
    {
        // 禁用標題格式化，保留中文標題
        HeadingRowFormatter::default('none');
    }

    /**
     * 指定只處理第一個工作表 (index 0)
     */
    public function sheets(): array
    {
        return [
            0 => new ProductDataSheetImport(),
        ];
    }
}

/**
 * 商品匯入 - 資料工作表處理類別
 *
 * 負責實際的資料解析、驗證與儲存邏輯。
 * 只會被套用到 Excel 的第一個工作表（資料頁）。
 */
class ProductDataSheetImport implements ToModel, WithHeadingRow, WithValidation, WithMapping, SkipsEmptyRows
{
    private $categories;
    private $units;
    private $productService;

    public function __construct()
    {
        // 快取所有類別與單位，避免 N+1 查詢
        $this->categories = Category::pluck('id', 'name');
        $this->units = Unit::pluck('id', 'name');
        $this->productService = app(\App\Modules\Inventory\Contracts\ProductServiceInterface::class);
    }

    /**
     * 資料映射：將 Excel 原始標題（含「(選填)」）對應到乾淨的鍵名
     *
     * 注意：WithValidation 驗證的是 map() 之前的原始資料，
     * 因此 rules() 中的鍵名必須匹配 Excel 的原始標題。
     * map() 的返回值只影響 model() 接收到的資料。
     */
    public function map($row): array
    {
        $code = $row['商品代號(選填)'] ?? $row['商品代號'] ?? null;
        $barcode = $row['條碼(選填)'] ?? $row['條碼'] ?? null;

        return [
            '商品代號' => $code !== null ? (string)$code : null,
            '條碼' => $barcode !== null ? (string)$barcode : null,
            '商品名稱' => $row['商品名稱'] ?? null,
            '類別名稱' => $row['類別名稱'] ?? null,
            '品牌' => $row['品牌'] ?? null,
            '規格' => $row['規格'] ?? null,
            '基本單位' => $row['基本單位'] ?? null,
            '大單位' => $row['大單位'] ?? null,
            '換算率' => isset($row['換算率']) ? (float)$row['換算率'] : null,
            '成本價' => isset($row['成本價']) ? (float)$row['成本價'] : null,
            '售價' => isset($row['售價']) ? (float)$row['售價'] : null,
            '會員價' => isset($row['會員價']) ? (float)$row['會員價'] : null,
            '批發價' => isset($row['批發價']) ? (float)$row['批發價'] : null,
        ];
    }

    /**
     * @param array $row (map() 回傳的乾淨鍵名陣列)
     */
    public function model(array $row)
    {
        // 查找關聯 ID
        $categoryId = $this->categories[$row['類別名稱']] ?? null;
        $baseUnitId = $this->units[$row['基本單位']] ?? null;
        $largeUnitId = isset($row['大單位']) ? ($this->units[$row['大單位']] ?? null) : null;

        // 若必要關聯找不到，理論上 Validation 會攔截，但此處做防禦性編程
        if (!$categoryId || !$baseUnitId) {
            return null;
        }

        $code = $row['商品代號'] ?? null;
        $barcode = $row['條碼'] ?? null;

        // Upsert 邏輯：透過 Service 統一查找與處理
        $product = $this->productService->findByBarcodeOrCode($barcode, $code);

        $data = [
            'name' => $row['商品名稱'],
            'category_id' => $categoryId,
            'brand' => $row['品牌'] ?? null,
            'specification' => $row['規格'] ?? null,
            'base_unit_id' => $baseUnitId,
            'large_unit_id' => $largeUnitId,
            'conversion_rate' => $row['換算率'] ?? null,
            'purchase_unit_id' => null,
            'cost_price' => $row['成本價'] ?? null,
            'price' => $row['售價'] ?? null,
            'member_price' => $row['會員價'] ?? null,
            'wholesale_price' => $row['批發價'] ?? null,
        ];

        if ($product) {
            $this->productService->updateProduct($product, $data);
        } else {
            if (!empty($code)) $data['code'] = $code;
            if (!empty($barcode)) $data['barcode'] = $barcode;
            $this->productService->createProduct($data);
        }

        return null; // 返回 null，因為 Service 已經處理完儲存
    }

    /**
     * 驗證規則
     *
     * 鍵名必須匹配 Excel 原始標題（含「(選填)」後綴），
     * 因為 WithValidation 驗證的是 map() 之前的原始資料。
     */
    public function rules(): array
    {
        return [
            '商品代號(選填)' => ['nullable', 'string', 'min:2', 'max:8'],
            '條碼(選填)' => ['nullable', 'string'],
            '商品名稱' => ['required', 'string'],
            '類別名稱' => ['required', function($attribute, $value, $fail) {
                if (!isset($this->categories[$value])) {
                    $fail("找不到類別: " . $value);
                }
            }],
            '基本單位' => ['required', function($attribute, $value, $fail) {
                if (!isset($this->units[$value])) {
                    $fail("找不到單位: " . $value);
                }
            }],
            '大單位' => ['nullable', function($attribute, $value, $fail) {
                if ($value && !isset($this->units[$value])) {
                    $fail("找不到單位: " . $value);
                }
            }],

            '換算率' => ['nullable', 'numeric', 'min:0.0001', 'required_with:大單位'],
            '成本價' => ['nullable', 'numeric', 'min:0'],
            '售價' => ['nullable', 'numeric', 'min:0'],
            '會員價' => ['nullable', 'numeric', 'min:0'],
            '批發價' => ['nullable', 'numeric', 'min:0'],
        ];
    }

    /**
     * 自訂驗證錯誤訊息的欄位名稱
     * 把含 "(選填)" 後綴的欄位顯示為友善名稱
     */
    public function customValidationAttributes(): array
    {
        return [
            '商品代號(選填)' => '商品代號',
            '條碼(選填)' => '條碼',
        ];
    }
}
