<?php

namespace App\Services;

use App\Helpers\AiTokenHelper;
use App\Models\System\AiConfig;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class BusinessCardOcrService
{
    /**
     * 명함 이미지에서 정보 추출
     */
    public function extractFromImage(string $base64Image): array
    {
        $config = AiConfig::getActiveGemini();

        if (! $config) {
            throw new \RuntimeException('Gemini API 설정이 없습니다. 시스템 설정에서 AI 설정을 추가해주세요.');
        }

        // 인증 방식에 따라 다른 API 호출
        if ($config->isVertexAi()) {
            return $this->callVertexAiApi($config, $base64Image);
        }

        return $this->callGoogleAiStudioApi($config, $base64Image);
    }

    /**
     * Vertex AI API 호출 (Google Cloud - 서비스 계정 인증)
     */
    private function callVertexAiApi(AiConfig $config, string $base64Image): array
    {
        $model = $config->model;
        $projectId = $config->getProjectId();
        $region = $config->getRegion();

        if (! $projectId) {
            throw new \RuntimeException('Vertex AI 프로젝트 ID가 설정되지 않았습니다.');
        }

        // 액세스 토큰 가져오기
        $accessToken = $this->getAccessToken($config);
        if (! $accessToken) {
            throw new \RuntimeException('Google Cloud 인증 실패');
        }

        // Vertex AI 엔드포인트
        $url = "https://{$region}-aiplatform.googleapis.com/v1/projects/{$projectId}/locations/{$region}/publishers/google/models/{$model}:generateContent";

        return $this->callGeminiApi($url, $base64Image, [
            'Authorization' => 'Bearer '.$accessToken,
            'Content-Type' => 'application/json',
        ], true);  // Vertex AI
    }

    /**
     * Google AI Studio API 호출 (API 키 인증)
     */
    private function callGoogleAiStudioApi(AiConfig $config, string $base64Image): array
    {
        $model = $config->model;
        $apiKey = $config->api_key;
        $baseUrl = $config->base_url ?? 'https://generativelanguage.googleapis.com/v1beta';

        $url = "{$baseUrl}/models/{$model}:generateContent?key={$apiKey}";

        return $this->callGeminiApi($url, $base64Image, [
            'Content-Type' => 'application/json',
        ], false);  // Google AI Studio
    }

    /**
     * Gemini API 공통 호출 로직
     */
    private function callGeminiApi(string $url, string $base64Image, array $headers, bool $isVertexAi = false): array
    {
        // Base64 데이터에서 prefix 제거
        $imageData = $base64Image;
        $mimeType = 'image/jpeg';

        if (preg_match('/^data:(image\/\w+);base64,/', $base64Image, $matches)) {
            $mimeType = $matches[1];
            $imageData = preg_replace('/^data:image\/\w+;base64,/', '', $base64Image);
        }

        $prompt = $this->buildPrompt();

        // Vertex AI는 role 필드 필요
        $content = [
            'parts' => [
                [
                    'inlineData' => [
                        'mimeType' => $mimeType,
                        'data' => $imageData,
                    ],
                ],
                [
                    'text' => $prompt,
                ],
            ],
        ];

        if ($isVertexAi) {
            $content['role'] = 'user';
        }

        try {
            $response = Http::timeout(30)
                ->withHeaders($headers)
                ->post($url, [
                    'contents' => [$content],
                    'generationConfig' => [
                        'temperature' => 0.1,
                        'topK' => 40,
                        'topP' => 0.95,
                        'maxOutputTokens' => 1024,
                        'responseMimeType' => 'application/json',
                    ],
                ]);

            if (! $response->successful()) {
                Log::error('Gemini API error', [
                    'status' => $response->status(),
                    'body' => $response->body(),
                ]);
                throw new \RuntimeException('AI API 호출 실패: '.$response->status());
            }

            $result = $response->json();

            // 토큰 사용량 저장
            AiTokenHelper::saveGeminiUsage($result, $result['modelVersion'] ?? 'gemini', '명함OCR');

            $text = $result['candidates'][0]['content']['parts'][0]['text'] ?? '';

            // JSON 파싱
            $parsed = json_decode($text, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                Log::warning('AI response JSON parse failed', ['text' => $text]);
                throw new \RuntimeException('AI 응답 파싱 실패');
            }

            return [
                'ok' => true,
                'data' => $this->normalizeData($parsed),
                'raw_response' => $text,
            ];
        } catch (ConnectionException $e) {
            Log::error('Gemini API connection failed', ['error' => $e->getMessage()]);
            throw new \RuntimeException('AI API 연결 실패');
        }
    }

    /**
     * 서비스 계정으로 OAuth2 액세스 토큰 가져오기
     */
    private function getAccessToken(AiConfig $config): ?string
    {
        // DB에서 서비스 계정 경로 가져오기
        $configuredPath = $config->getServiceAccountPath();

        // 여러 경로에서 서비스 계정 파일 찾기
        $possiblePaths = array_filter([
            $configuredPath,  // DB에 설정된 경로 우선
            '/var/www/sales/apikey/google_service_account.json',
            storage_path('app/google_service_account.json'),
        ]);

        $serviceAccountPath = null;
        foreach ($possiblePaths as $path) {
            if ($path && file_exists($path)) {
                $serviceAccountPath = $path;
                break;
            }
        }

        if (! $serviceAccountPath) {
            Log::error('Service account file not found', ['tried_paths' => $possiblePaths]);

            return null;
        }

        $serviceAccount = json_decode(file_get_contents($serviceAccountPath), true);
        if (! $serviceAccount) {
            Log::error('Service account JSON parse failed');

            return null;
        }

        // JWT 생성
        $now = time();
        $jwtHeader = $this->base64UrlEncode(json_encode(['alg' => 'RS256', 'typ' => 'JWT']));
        $jwtClaim = $this->base64UrlEncode(json_encode([
            'iss' => $serviceAccount['client_email'],
            'scope' => 'https://www.googleapis.com/auth/cloud-platform',
            'aud' => 'https://oauth2.googleapis.com/token',
            'exp' => $now + 3600,
            'iat' => $now,
        ]));

        $privateKey = openssl_pkey_get_private($serviceAccount['private_key']);
        if (! $privateKey) {
            Log::error('Failed to load private key');

            return null;
        }

        openssl_sign($jwtHeader.'.'.$jwtClaim, $signature, $privateKey, OPENSSL_ALGO_SHA256);
        $jwt = $jwtHeader.'.'.$jwtClaim.'.'.$this->base64UrlEncode($signature);

        // OAuth 토큰 요청
        try {
            $response = Http::asForm()->post('https://oauth2.googleapis.com/token', [
                'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
                'assertion' => $jwt,
            ]);

            if ($response->successful()) {
                $data = $response->json();

                return $data['access_token'] ?? null;
            }

            Log::error('OAuth token request failed', [
                'status' => $response->status(),
                'body' => $response->body(),
            ]);

            return null;
        } catch (\Exception $e) {
            Log::error('OAuth token request exception', ['error' => $e->getMessage()]);

            return null;
        }
    }

    /**
     * Base64 URL 인코딩
     */
    private function base64UrlEncode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    /**
     * OCR 프롬프트 생성
     */
    private function buildPrompt(): string
    {
        return <<<'PROMPT'
이 명함 이미지에서 다음 정보를 추출해주세요.

## 추출 항목
1. company_name: 회사명/상호
2. ceo_name: 대표자명/담당자명 (명함에 있는 이름)
3. business_number: 사업자등록번호 (000-00-00000 형식)
4. contact_phone: 연락처/전화번호
5. contact_email: 이메일
6. address: 주소
7. position: 직책
8. department: 부서

## 규칙
1. 정보가 없으면 빈 문자열("")로 응답
2. 사업자번호는 10자리 숫자를 000-00-00000 형식으로 변환
3. 전화번호는 하이픈 포함 형식 유지
4. 한국어로 된 정보를 우선 추출

## 출력 형식 (JSON)
{
  "company_name": "",
  "ceo_name": "",
  "business_number": "",
  "contact_phone": "",
  "contact_email": "",
  "address": "",
  "position": "",
  "department": ""
}

JSON 형식으로만 응답하세요.
PROMPT;
    }

    /**
     * 추출된 데이터 정규화
     */
    private function normalizeData(array $data): array
    {
        // 사업자번호 정규화
        if (! empty($data['business_number'])) {
            $digits = preg_replace('/\D/', '', $data['business_number']);
            if (strlen($digits) === 10) {
                $data['business_number'] = substr($digits, 0, 3).'-'.substr($digits, 3, 2).'-'.substr($digits, 5);
            }
        }

        return [
            'company_name' => trim($data['company_name'] ?? ''),
            'ceo_name' => trim($data['ceo_name'] ?? ''),
            'business_number' => trim($data['business_number'] ?? ''),
            'contact_phone' => trim($data['contact_phone'] ?? ''),
            'contact_email' => trim($data['contact_email'] ?? ''),
            'address' => trim($data['address'] ?? ''),
            'position' => trim($data['position'] ?? ''),
            'department' => trim($data['department'] ?? ''),
        ];
    }
}
