<?php

namespace App\Services\Calculation;

use Illuminate\Support\Facades\Log;

class FormulaParser
{
    /**
     * 계산식 실행
     *
     * @param  string  $formula  계산식 표현식
     * @param  array  $variables  변수 값들
     * @return array|float 계산 결과
     */
    public function execute(string $formula, array $variables): array|float
    {
        try {
            // 안전한 계산식 실행을 위한 파싱
            $result = $this->parseAndExecute($formula, $variables);

            if (is_array($result)) {
                return $result;
            }

            return ['result' => $result];

        } catch (\Exception $e) {
            Log::error('계산식 실행 실패', [
                'formula' => $formula,
                'variables' => $variables,
                'error' => $e->getMessage(),
            ]);

            throw new \RuntimeException("계산식 실행 실패: {$e->getMessage()}");
        }
    }

    /**
     * 계산식 파싱 및 실행
     */
    protected function parseAndExecute(string $formula, array $variables): array|float
    {
        // 미리 정의된 함수 패턴들 처리
        if ($this->isPreDefinedFunction($formula)) {
            return $this->executePreDefinedFunction($formula, $variables);
        }

        // 단순 수학 표현식 처리
        if ($this->isSimpleMathExpression($formula)) {
            return $this->executeSimpleMath($formula, $variables);
        }

        // 조건식 처리 (IF문 등)
        if ($this->isConditionalExpression($formula)) {
            return $this->executeConditionalExpression($formula, $variables);
        }

        throw new \InvalidArgumentException("지원되지 않는 계산식 형태: {$formula}");
    }

    /**
     * 미리 정의된 함수 실행
     */
    protected function executePreDefinedFunction(string $formula, array $variables): array
    {
        // 경동기업 스크린 제작사이즈 계산
        if ($formula === 'kyungdong_screen_size') {
            return [
                'W1' => ($variables['W0'] ?? 0) + 160,
                'H1' => ($variables['H0'] ?? 0) + 350,
            ];
        }

        // 경동기업 철재 제작사이즈 계산
        if ($formula === 'kyungdong_steel_size') {
            return [
                'W1' => ($variables['W0'] ?? 0) + 110,
                'H1' => ($variables['H0'] ?? 0) + 350,
            ];
        }

        // 스크린 중량 계산
        if ($formula === 'screen_weight_calculation') {
            $W0 = $variables['W0'] ?? 0;
            $W1 = $variables['W1'] ?? 0;
            $H1 = $variables['H1'] ?? 0;
            $area = ($W1 * $H1) / 1000000;

            return [
                'area' => $area,
                'weight' => ($area * 2) + ($W0 / 1000 * 14.17),
            ];
        }

        // 브라켓 수량 계산
        if ($formula === 'bracket_quantity') {
            $W1 = $variables['W1'] ?? 0;
            if ($W1 <= 3000) {
                return ['result' => 2];
            }
            if ($W1 <= 6000) {
                return ['result' => 3];
            }
            if ($W1 <= 9000) {
                return ['result' => 4];
            }
            if ($W1 <= 12000) {
                return ['result' => 5];
            }

            return ['result' => 5]; // 최대값
        }

        // 5130 시스템 브라켓 사이즈 계산 (중량+인치 기반)
        if ($formula === 'motor_bracket_size') {
            return $this->calculateMotorBracketSize($variables);
        }

        // 환봉 수량 계산
        if ($formula === 'round_bar_quantity') {
            $W1 = $variables['W1'] ?? 0;
            $qty = $variables['qty'] ?? 1;

            if ($W1 <= 3000) {
                return ['result' => 1 * $qty];
            }
            if ($W1 <= 6000) {
                return ['result' => 2 * $qty];
            }
            if ($W1 <= 9000) {
                return ['result' => 3 * $qty];
            }
            if ($W1 <= 12000) {
                return ['result' => 4 * $qty];
            }

            return ['result' => 4 * $qty];
        }

        // 샤프트 규격 결정
        if ($formula === 'shaft_size_determination') {
            $W1 = $variables['W1'] ?? 0;

            if ($W1 <= 6000) {
                return ['result' => 4];
            } // 4인치
            if ($W1 <= 8200) {
                return ['result' => 5];
            } // 5인치

            return ['result' => 0]; // 미정의
        }

        // 모터 용량 결정
        if ($formula === 'motor_capacity_determination') {
            $shaftSize = $variables['shaft_size'] ?? 4;
            $weight = $variables['weight'] ?? 0;

            // 샤프트별 중량 매트릭스
            if ($shaftSize == 4) {
                if ($weight <= 150) {
                    return ['result' => '150K'];
                }
                if ($weight <= 300) {
                    return ['result' => '300K'];
                }
                if ($weight <= 400) {
                    return ['result' => '400K'];
                }
            } elseif ($shaftSize == 5) {
                if ($weight <= 123) {
                    return ['result' => '150K'];
                }
                if ($weight <= 246) {
                    return ['result' => '300K'];
                }
                if ($weight <= 327) {
                    return ['result' => '400K'];
                }
                if ($weight <= 500) {
                    return ['result' => '500K'];
                }
                if ($weight <= 600) {
                    return ['result' => '600K'];
                }
            } elseif ($shaftSize == 6) {
                if ($weight <= 104) {
                    return ['result' => '150K'];
                }
                if ($weight <= 208) {
                    return ['result' => '300K'];
                }
                if ($weight <= 300) {
                    return ['result' => '400K'];
                }
                if ($weight <= 424) {
                    return ['result' => '500K'];
                }
                if ($weight <= 508) {
                    return ['result' => '600K'];
                }
                if ($weight <= 800) {
                    return ['result' => '800K'];
                }
                if ($weight <= 1000) {
                    return ['result' => '1000K'];
                }
            }

            return ['result' => '미정의'];
        }

        throw new \InvalidArgumentException("알 수 없는 미리 정의된 함수: {$formula}");
    }

    /**
     * 단순 수학 표현식 실행
     */
    protected function executeSimpleMath(string $formula, array $variables): float
    {
        // 변수 치환
        $expression = $formula;
        foreach ($variables as $key => $value) {
            $expression = str_replace($key, (string) $value, $expression);
        }

        // 안전한 수학 표현식 검증
        if (! $this->isSafeMathExpression($expression)) {
            throw new \InvalidArgumentException("안전하지 않은 수학 표현식: {$expression}");
        }

        // 계산 실행
        return eval("return {$expression};");
    }

    /**
     * 조건식 실행
     */
    protected function executeConditionalExpression(string $formula, array $variables): float
    {
        // 간단한 IF 조건식 파싱
        // 예: "IF(W1 <= 3000, 2, IF(W1 <= 6000, 3, 4))"

        $pattern = '/IF\s*\(\s*([^,]+),\s*([^,]+),\s*(.+)\)/i';

        if (preg_match($pattern, $formula, $matches)) {
            $condition = trim($matches[1]);
            $trueValue = trim($matches[2]);
            $falseValue = trim($matches[3]);

            // 조건 평가
            if ($this->evaluateCondition($condition, $variables)) {
                return is_numeric($trueValue) ? (float) $trueValue : $this->execute($trueValue, $variables)['result'];
            } else {
                return is_numeric($falseValue) ? (float) $falseValue : $this->execute($falseValue, $variables)['result'];
            }
        }

        throw new \InvalidArgumentException("지원되지 않는 조건식: {$formula}");
    }

    /**
     * 조건 평가
     */
    protected function evaluateCondition(string $condition, array $variables): bool
    {
        // 변수 치환
        $expression = $condition;
        foreach ($variables as $key => $value) {
            $expression = str_replace($key, (string) $value, $expression);
        }

        // 안전한 조건식 검증
        if (! $this->isSafeConditionExpression($expression)) {
            throw new \InvalidArgumentException("안전하지 않은 조건식: {$expression}");
        }

        return eval("return {$expression};");
    }

    /**
     * 5130 시스템 모터 브라켓 사이즈 계산 (중량+인치 기반)
     */
    protected function calculateMotorBracketSize(array $variables): array
    {
        $weight = floatval($variables['weight'] ?? 0);
        $inch = is_numeric($variables['inch'] ?? null) ? intval($variables['inch']) : 0;

        $motorCapacity = 0;

        if ($inch > 0) {
            // 중량 + 인치 기준 판단 (철재 기준)
            if (
                ($inch == 4 && $weight <= 300) ||
                ($inch == 5 && $weight <= 246) ||
                ($inch == 6 && $weight <= 208)
            ) {
                $motorCapacity = 300;
            } elseif (
                ($inch == 4 && $weight > 300 && $weight <= 400) ||
                ($inch == 5 && $weight > 246 && $weight <= 327) ||
                ($inch == 6 && $weight > 208 && $weight <= 277)
            ) {
                $motorCapacity = 400;
            } elseif (
                ($inch == 5 && $weight > 327 && $weight <= 500) ||
                ($inch == 6 && $weight > 277 && $weight <= 424) ||
                ($inch == 8 && $weight <= 324)
            ) {
                $motorCapacity = 500;
            } elseif (
                ($inch == 5 && $weight > 500 && $weight <= 600) ||
                ($inch == 6 && $weight > 424 && $weight <= 508) ||
                ($inch == 8 && $weight > 324 && $weight <= 388)
            ) {
                $motorCapacity = 600;
            } elseif (
                ($inch == 6 && $weight > 600 && $weight <= 800) ||
                ($inch == 6 && $weight > 508 && $weight <= 800) ||
                ($inch == 8 && $weight > 388 && $weight <= 611)
            ) {
                $motorCapacity = 800;
            } elseif (
                ($inch == 6 && $weight > 800 && $weight <= 1000) ||
                ($inch == 8 && $weight > 611 && $weight <= 1000)
            ) {
                $motorCapacity = 1000;
            }
        } else {
            // 인치가 없으면 중량만으로 판단
            if ($weight <= 300) {
                $motorCapacity = 300;
            } elseif ($weight <= 400) {
                $motorCapacity = 400;
            } elseif ($weight <= 500) {
                $motorCapacity = 500;
            } elseif ($weight <= 600) {
                $motorCapacity = 600;
            } elseif ($weight <= 800) {
                $motorCapacity = 800;
            } elseif ($weight <= 1000) {
                $motorCapacity = 1000;
            }
        }

        // 용량별 브라켓 사이즈 매핑
        $bracketSize = '530*320'; // 기본값
        if (in_array($motorCapacity, [300, 400])) {
            $bracketSize = '530*320';
        } elseif (in_array($motorCapacity, [500, 600])) {
            $bracketSize = '600*350';
        } elseif (in_array($motorCapacity, [800, 1000])) {
            $bracketSize = '690*390';
        }

        return [
            'bracket_size' => $bracketSize,
            'motor_capacity' => $motorCapacity,
            'calculated_weight' => $weight,
            'shaft_inch' => $inch,
        ];
    }

    /**
     * 미리 정의된 함수인지 확인
     */
    protected function isPreDefinedFunction(string $formula): bool
    {
        $predefinedFunctions = [
            'kyungdong_screen_size',
            'kyungdong_steel_size',
            'screen_weight_calculation',
            'bracket_quantity',
            'motor_bracket_size',
            'round_bar_quantity',
            'shaft_size_determination',
            'motor_capacity_determination',
        ];

        return in_array($formula, $predefinedFunctions);
    }

    /**
     * 단순 수학 표현식인지 확인
     */
    protected function isSimpleMathExpression(string $formula): bool
    {
        return preg_match('/^[A-Za-z0-9_+\-*\/().\s]+$/', $formula);
    }

    /**
     * 조건식인지 확인
     */
    protected function isConditionalExpression(string $formula): bool
    {
        return preg_match('/IF\s*\(/i', $formula);
    }

    /**
     * 안전한 수학 표현식인지 검증
     */
    protected function isSafeMathExpression(string $expression): bool
    {
        // 위험한 함수나 키워드 차단
        $dangerous = ['exec', 'system', 'shell_exec', 'eval', 'file', 'fopen', 'include', 'require'];

        foreach ($dangerous as $func) {
            if (stripos($expression, $func) !== false) {
                return false;
            }
        }

        // 허용된 문자만 포함하는지 확인
        return preg_match('/^[0-9+\-*\/().\s]+$/', $expression);
    }

    /**
     * 안전한 조건식인지 검증
     */
    protected function isSafeConditionExpression(string $expression): bool
    {
        // 허용된 연산자: ==, !=, <, >, <=, >=, &&, ||
        $allowedPattern = '/^[0-9+\-*\/().\s<>=!&|]+$/';

        return preg_match($allowedPattern, $expression);
    }
}
