<?php

namespace App\Helpers;

/**
 * 5130 레거시 시스템 호환 계산기
 *
 * 5130 시스템의 견적 계산 로직을 SAM에서 동일하게 재현하기 위한 헬퍼 클래스입니다.
 * 데이터 마이그레이션 후 계산 결과 동일성 검증에 사용됩니다.
 *
 * 주요 기능:
 * - 절곡품 단가 계산 (getBendPlatePrice)
 * - 모터 용량 계산 (calculateMotorSpec)
 * - 두께 매핑 (normalizeThickness)
 * - 면적 계산 (calculateArea)
 *
 * @see docs/plans/5130-sam-data-migration-plan.md 섹션 4.5
 */
class Legacy5130Calculator
{
    // =========================================================================
    // 두께 매핑 상수
    // =========================================================================

    /**
     * EGI(아연도금) 두께 매핑
     * 5130: 1.15 → 1.2, 1.55 → 1.6
     */
    public const EGI_THICKNESS_MAP = [
        1.15 => 1.2,
        1.55 => 1.6,
    ];

    /**
     * SUS(스테인리스) 두께 매핑
     * 5130: 1.15 → 1.2, 1.55 → 1.5
     */
    public const SUS_THICKNESS_MAP = [
        1.15 => 1.2,
        1.55 => 1.5,
    ];

    // =========================================================================
    // 모터 용량 상수
    // =========================================================================

    /**
     * 스크린 모터 용량 테이블
     * [min_weight, max_weight, min_bracket_inch, max_bracket_inch] => capacity
     */
    public const SCREEN_MOTOR_CAPACITY = [
        // 150K: 중량 ≤20, 브라켓인치 ≤6
        ['weight_max' => 20, 'bracket_max' => 6, 'capacity' => '150K'],
        // 200K: 중량 ≤35, 브라켓인치 ≤8
        ['weight_max' => 35, 'bracket_max' => 8, 'capacity' => '200K'],
        // 300K: 중량 ≤50, 브라켓인치 ≤10
        ['weight_max' => 50, 'bracket_max' => 10, 'capacity' => '300K'],
        // 400K: 중량 ≤70, 브라켓인치 ≤12
        ['weight_max' => 70, 'bracket_max' => 12, 'capacity' => '400K'],
        // 500K: 중량 ≤90, 브라켓인치 ≤14
        ['weight_max' => 90, 'bracket_max' => 14, 'capacity' => '500K'],
        // 600K: 그 이상
        ['weight_max' => PHP_INT_MAX, 'bracket_max' => PHP_INT_MAX, 'capacity' => '600K'],
    ];

    /**
     * 철재 모터 용량 테이블
     */
    public const STEEL_MOTOR_CAPACITY = [
        // 300K: 중량 ≤40, 브라켓인치 ≤8
        ['weight_max' => 40, 'bracket_max' => 8, 'capacity' => '300K'],
        // 400K: 중량 ≤60, 브라켓인치 ≤10
        ['weight_max' => 60, 'bracket_max' => 10, 'capacity' => '400K'],
        // 500K: 중량 ≤80, 브라켓인치 ≤12
        ['weight_max' => 80, 'bracket_max' => 12, 'capacity' => '500K'],
        // 600K: 중량 ≤100, 브라켓인치 ≤14
        ['weight_max' => 100, 'bracket_max' => 14, 'capacity' => '600K'],
        // 800K: 중량 ≤150, 브라켓인치 ≤16
        ['weight_max' => 150, 'bracket_max' => 16, 'capacity' => '800K'],
        // 1000K: 그 이상
        ['weight_max' => PHP_INT_MAX, 'bracket_max' => PHP_INT_MAX, 'capacity' => '1000K'],
    ];

    /**
     * 브라켓 사이즈 매핑 (용량 → 사이즈)
     */
    public const BRACKET_SIZE_MAP = [
        '150K' => ['width' => 450, 'height' => 280],
        '200K' => ['width' => 480, 'height' => 300],
        '300K' => ['width' => 530, 'height' => 320],
        '400K' => ['width' => 530, 'height' => 320],
        '500K' => ['width' => 600, 'height' => 350],
        '600K' => ['width' => 600, 'height' => 350],
        '800K' => ['width' => 690, 'height' => 390],
        '1000K' => ['width' => 690, 'height' => 390],
    ];

    // =========================================================================
    // 두께 정규화
    // =========================================================================

    /**
     * 두께 정규화 (5130 방식)
     *
     * @param  string  $material  재질 (EGI, SUS)
     * @param  float  $thickness  입력 두께
     * @return float 정규화된 두께
     */
    public static function normalizeThickness(string $material, float $thickness): float
    {
        $material = strtoupper($material);

        if ($material === 'EGI' && isset(self::EGI_THICKNESS_MAP[$thickness])) {
            return self::EGI_THICKNESS_MAP[$thickness];
        }

        if ($material === 'SUS' && isset(self::SUS_THICKNESS_MAP[$thickness])) {
            return self::SUS_THICKNESS_MAP[$thickness];
        }

        return $thickness;
    }

    // =========================================================================
    // 면적 계산
    // =========================================================================

    /**
     * 면적 계산 (mm² → m²)
     *
     * 5130 방식: (length × width) / 1,000,000
     *
     * @param  float  $length  길이 (mm)
     * @param  float  $width  너비 (mm)
     * @return float 면적 (m²)
     */
    public static function calculateArea(float $length, float $width): float
    {
        return ($length * $width) / 1000000;
    }

    /**
     * 면적 계산 (개구부 → 제작 사이즈)
     *
     * @param  float  $W0  개구부 폭 (mm)
     * @param  float  $H0  개구부 높이 (mm)
     * @param  string  $productType  제품 유형 (screen, steel)
     * @return array ['W1' => 제작폭, 'H1' => 제작높이, 'area' => 면적(m²)]
     */
    public static function calculateManufacturingSize(float $W0, float $H0, string $productType = 'screen'): array
    {
        $productType = strtolower($productType);

        // 마진 값 결정
        if ($productType === 'steel') {
            $marginW = 110;
            $marginH = 350;
        } else {
            // screen (기본값)
            $marginW = 140;
            $marginH = 350;
        }

        $W1 = $W0 + $marginW;
        $H1 = $H0 + $marginH;
        $area = self::calculateArea($W1, $H1);

        return [
            'W1' => $W1,
            'H1' => $H1,
            'area' => $area,
        ];
    }

    // =========================================================================
    // 중량 계산
    // =========================================================================

    /**
     * 중량 계산 (5130 방식)
     *
     * @param  float  $W0  개구부 폭 (mm)
     * @param  float  $area  면적 (m²)
     * @param  string  $productType  제품 유형 (screen, steel)
     * @return float 중량 (kg)
     */
    public static function calculateWeight(float $W0, float $area, string $productType = 'screen'): float
    {
        $productType = strtolower($productType);

        if ($productType === 'steel') {
            // 철재: 면적 × 25 kg/m²
            return $area * 25;
        }

        // 스크린: (면적 × 2) + (폭(m) × 14.17)
        return ($area * 2) + (($W0 / 1000) * 14.17);
    }

    // =========================================================================
    // 절곡품 단가 계산
    // =========================================================================

    /**
     * 절곡품 단가 계산 (5130 getBendPlatePrice 호환)
     *
     * @param  string  $material  재질 (EGI, SUS)
     * @param  float  $thickness  두께 (mm)
     * @param  float  $length  길이 (mm)
     * @param  float  $width  너비 (mm)
     * @param  int  $qty  수량
     * @param  float  $unitPricePerM2  단위 면적당 단가 (원/m²)
     * @return array ['area' => 면적, 'total' => 총액]
     */
    public static function getBendPlatePrice(
        string $material,
        float $thickness,
        float $length,
        float $width,
        int $qty,
        float $unitPricePerM2
    ): array {
        // 1. 두께 정규화
        $normalizedThickness = self::normalizeThickness($material, $thickness);

        // 2. 면적 계산 (mm² → m²)
        $areaM2 = self::calculateArea($length, $width);

        // 3. 총액 계산 (절삭 - Math.floor 호환)
        $total = floor($unitPricePerM2 * $areaM2 * $qty);

        return [
            'material' => $material,
            'original_thickness' => $thickness,
            'normalized_thickness' => $normalizedThickness,
            'length' => $length,
            'width' => $width,
            'area_m2' => $areaM2,
            'qty' => $qty,
            'unit_price_per_m2' => $unitPricePerM2,
            'total' => $total,
        ];
    }

    // =========================================================================
    // 모터 용량 계산
    // =========================================================================

    /**
     * 모터 용량 계산 (5130 calculateMotorSpec 호환)
     *
     * @param  float  $weight  중량 (kg)
     * @param  float  $bracketInch  브라켓 인치
     * @param  string  $productType  제품 유형 (screen, steel)
     * @return array ['capacity' => 용량, 'bracket_size' => 브라켓 사이즈]
     */
    public static function calculateMotorSpec(float $weight, float $bracketInch, string $productType = 'screen'): array
    {
        $productType = strtolower($productType);

        // 용량 테이블 선택
        $capacityTable = ($productType === 'steel')
            ? self::STEEL_MOTOR_CAPACITY
            : self::SCREEN_MOTOR_CAPACITY;

        // 용량 결정 (경계값은 상위 용량 적용)
        $capacity = null;
        foreach ($capacityTable as $entry) {
            if ($weight <= $entry['weight_max'] && $bracketInch <= $entry['bracket_max']) {
                $capacity = $entry['capacity'];
                break;
            }
        }

        // 기본값 (마지막 항목)
        if ($capacity === null) {
            $lastEntry = end($capacityTable);
            $capacity = $lastEntry['capacity'];
        }

        // 브라켓 사이즈 조회
        $bracketSize = self::BRACKET_SIZE_MAP[$capacity] ?? ['width' => 530, 'height' => 320];

        return [
            'product_type' => $productType,
            'weight' => $weight,
            'bracket_inch' => $bracketInch,
            'capacity' => $capacity,
            'bracket_size' => $bracketSize,
            'bracket_dimensions' => "{$bracketSize['width']}×{$bracketSize['height']}",
        ];
    }

    /**
     * 품목 코드로 제품 유형 판별 (5130 방식)
     *
     * @param  string  $itemCode  품목 코드 (예: KS-001, ST-002)
     * @return string 제품 유형 (screen, steel)
     */
    public static function detectProductType(string $itemCode): string
    {
        $prefix = strtoupper(substr($itemCode, 0, 2));

        // KS, KW로 시작하면 스크린
        if (in_array($prefix, ['KS', 'KW'])) {
            return 'screen';
        }

        // 그 외는 철재
        return 'steel';
    }

    // =========================================================================
    // 가이드레일/샤프트/파이프 계산
    // =========================================================================

    /**
     * 가이드레일 수량 계산 (5130 calculateGuidrail 호환)
     *
     * @param  float  $height  높이 (mm)
     * @param  float  $standardLength  기본 길이 (mm, 기본값 3490)
     * @return int 가이드레일 수량
     */
    public static function calculateGuiderailQty(float $height, float $standardLength = 3490): int
    {
        if ($standardLength <= 0) {
            return 1;
        }

        return (int) ceil($height / $standardLength);
    }

    // =========================================================================
    // 비인정 자재 단가 계산
    // =========================================================================

    /**
     * 비인정 스크린 단가 계산
     *
     * @param  float  $width  너비 (mm)
     * @param  float  $height  높이 (mm)
     * @param  int  $qty  수량
     * @param  float  $unitPricePerM2  단위 면적당 단가 (원/m²)
     * @return array ['area' => 면적, 'total' => 총액]
     */
    public static function calculateUnapprovedScreenPrice(
        float $width,
        float $height,
        int $qty,
        float $unitPricePerM2
    ): array {
        $areaM2 = self::calculateArea($width, $height);
        $total = floor($unitPricePerM2 * $areaM2 * $qty);

        return [
            'width' => $width,
            'height' => $height,
            'area_m2' => $areaM2,
            'qty' => $qty,
            'unit_price_per_m2' => $unitPricePerM2,
            'total' => $total,
        ];
    }

    /**
     * 철재 스라트 비인정 단가 계산
     *
     * @param  string  $type  유형 (방화셔터, 방범셔터, 단열셔터, 이중파이프, 조인트바)
     * @param  float  $width  너비 (mm)
     * @param  float  $height  높이 (mm)
     * @param  int  $qty  수량
     * @param  float  $unitPrice  단가
     * @return array ['surang' => 수량, 'total' => 총액]
     */
    public static function calculateUnapprovedSteelSlatPrice(
        string $type,
        float $width,
        float $height,
        int $qty,
        float $unitPrice
    ): array {
        // 면적 기준 유형
        $areaBased = ['방화셔터', '방범셔터', '단열셔터', '이중파이프'];

        if (in_array($type, $areaBased)) {
            // 면적 × 수량
            $areaM2 = self::calculateArea($width, $height);
            $surang = $areaM2 * $qty;
        } else {
            // 수량 기준 (조인트바 등)
            $surang = $qty;
        }

        $total = floor($unitPrice * $surang);

        return [
            'type' => $type,
            'width' => $width,
            'height' => $height,
            'qty' => $qty,
            'surang' => $surang,
            'unit_price' => $unitPrice,
            'total' => $total,
        ];
    }

    // =========================================================================
    // 전체 견적 계산 (통합)
    // =========================================================================

    /**
     * 전체 견적 계산 (5130 호환 모드)
     *
     * 5130의 계산 로직을 그대로 재현하여 동일한 결과를 반환합니다.
     *
     * @param  float  $W0  개구부 폭 (mm)
     * @param  float  $H0  개구부 높이 (mm)
     * @param  int  $qty  수량
     * @param  string  $productType  제품 유형 (screen, steel)
     * @return array 계산 결과
     */
    public static function calculateEstimate(
        float $W0,
        float $H0,
        int $qty,
        string $productType = 'screen'
    ): array {
        // 1. 제작 사이즈 계산
        $size = self::calculateManufacturingSize($W0, $H0, $productType);
        $W1 = $size['W1'];
        $H1 = $size['H1'];
        $area = $size['area'];

        // 2. 중량 계산
        $weight = self::calculateWeight($W0, $area, $productType);

        // 3. 브라켓 인치 계산 (폭 기준, 25.4mm = 1inch)
        $bracketInch = ceil($W1 / 25.4);

        // 4. 모터 용량 계산
        $motorSpec = self::calculateMotorSpec($weight, $bracketInch, $productType);

        return [
            'input' => [
                'W0' => $W0,
                'H0' => $H0,
                'qty' => $qty,
                'product_type' => $productType,
            ],
            'calculated' => [
                'W1' => $W1,
                'H1' => $H1,
                'area_m2' => round($area, 4),
                'weight_kg' => round($weight, 2),
                'bracket_inch' => $bracketInch,
            ],
            'motor' => $motorSpec,
        ];
    }

    // =========================================================================
    // 검증 유틸리티
    // =========================================================================

    /**
     * 5130 계산 결과와 비교 검증
     *
     * @param  array  $samResult  SAM 계산 결과
     * @param  array  $legacy5130Result  5130 계산 결과
     * @param  float  $tolerance  허용 오차 (기본 0.01 = 1%)
     * @return array ['match' => bool, 'differences' => array]
     */
    public static function validateAgainstLegacy(
        array $samResult,
        array $legacy5130Result,
        float $tolerance = 0.01
    ): array {
        $differences = [];

        // 비교할 필드 목록
        $compareFields = ['W1', 'H1', 'area_m2', 'weight_kg', 'total'];

        foreach ($compareFields as $field) {
            $samValue = $samResult[$field] ?? $samResult['calculated'][$field] ?? null;
            $legacyValue = $legacy5130Result[$field] ?? null;

            if ($samValue === null || $legacyValue === null) {
                continue;
            }

            $diff = abs($samValue - $legacyValue);
            $percentDiff = $legacyValue != 0 ? ($diff / abs($legacyValue)) : ($diff > 0 ? 1 : 0);

            if ($percentDiff > $tolerance) {
                $differences[$field] = [
                    'sam' => $samValue,
                    'legacy' => $legacyValue,
                    'diff' => $diff,
                    'percent_diff' => round($percentDiff * 100, 2).'%',
                ];
            }
        }

        return [
            'match' => empty($differences),
            'differences' => $differences,
        ];
    }
}
