<?php

namespace App\Services;

use App\Helpers\AiTokenHelper;
use App\Models\AiVoiceRecording;
use App\Models\System\AiConfig;
use Illuminate\Http\UploadedFile;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class AiVoiceRecordingService
{
    public function __construct(
        private GoogleCloudService $googleCloudService
    ) {}

    /**
     * 목록 조회
     */
    public function getList(array $params = []): LengthAwarePaginator
    {
        $query = AiVoiceRecording::query()
            ->with('user:id,name')
            ->orderBy('created_at', 'desc');

        if (! empty($params['status'])) {
            $query->where('status', $params['status']);
        }

        if (! empty($params['search'])) {
            $search = $params['search'];
            $query->where(function ($q) use ($search) {
                $q->where('title', 'like', "%{$search}%")
                    ->orWhere('transcript_text', 'like', "%{$search}%")
                    ->orWhere('analysis_text', 'like', "%{$search}%");
            });
        }

        $perPage = $params['per_page'] ?? 10;

        return $query->paginate($perPage);
    }

    /**
     * 상세 조회
     */
    public function getById(int $id): ?AiVoiceRecording
    {
        return AiVoiceRecording::with('user:id,name')->find($id);
    }

    /**
     * 새 녹음 레코드 생성
     */
    public function create(array $data): AiVoiceRecording
    {
        return AiVoiceRecording::create([
            'tenant_id' => session('selected_tenant_id'),
            'user_id' => Auth::id(),
            'title' => $data['title'] ?? '무제 음성녹음',
            'interview_template_id' => $data['interview_template_id'] ?? null,
            'status' => AiVoiceRecording::STATUS_PENDING,
            'file_expiry_date' => now()->addDays(7),
        ]);
    }

    /**
     * Base64 오디오 업로드 + 처리 파이프라인
     */
    public function processAudio(AiVoiceRecording $recording, string $audioBase64, int $durationSeconds): array
    {
        try {
            $recording->update([
                'status' => AiVoiceRecording::STATUS_PROCESSING,
                'duration_seconds' => $durationSeconds,
            ]);

            // 1. GCS에 오디오 업로드
            $objectName = sprintf(
                'voice-recordings/%d/%d/%s.webm',
                $recording->tenant_id,
                $recording->id,
                now()->format('YmdHis')
            );

            $uploadResult = $this->googleCloudService->uploadBase64Audio($audioBase64, $objectName);

            if (! $uploadResult) {
                throw new \Exception('오디오 파일 업로드 실패');
            }

            $gcsUri = $uploadResult['uri'];
            $fileSize = $uploadResult['size'] ?? 0;

            $recording->update([
                'audio_file_path' => $objectName,
                'audio_gcs_uri' => $gcsUri,
            ]);

            // GCS 업로드 토큰 사용량 기록
            AiTokenHelper::saveGcsStorageUsage('AI음성녹음-GCS저장', $fileSize);

            // 2. Speech-to-Text 변환
            Log::info('AiVoiceRecording STT 시작', ['recording_id' => $recording->id, 'gcs_uri' => $gcsUri]);
            $transcript = $this->googleCloudService->speechToText($gcsUri);
            Log::info('AiVoiceRecording STT 결과', ['recording_id' => $recording->id, 'transcript_length' => strlen($transcript ?? ''), 'transcript_preview' => mb_substr($transcript ?? '', 0, 100)]);

            if (! $transcript) {
                throw new \Exception('음성 인식 실패: STT 결과가 비어있습니다 (transcript='.var_export($transcript, true).')');
            }

            // STT 토큰 사용량 기록
            AiTokenHelper::saveSttUsage('AI음성녹음-STT변환', $durationSeconds);

            $recording->update(['transcript_text' => $transcript]);

            // 3. Gemini AI 분석
            $analysis = $this->analyzeWithGemini($transcript);

            $recording->update([
                'analysis_text' => $analysis,
                'status' => AiVoiceRecording::STATUS_COMPLETED,
            ]);

            return [
                'ok' => true,
                'recording' => $recording->fresh(),
            ];
        } catch (\Exception $e) {
            Log::error('AiVoiceRecording 처리 실패', [
                'recording_id' => $recording->id,
                'error' => $e->getMessage(),
            ]);

            $recording->update(['status' => AiVoiceRecording::STATUS_FAILED]);

            return [
                'ok' => false,
                'error' => $e->getMessage(),
            ];
        }
    }

    /**
     * 파일 업로드 처리
     */
    public function processUploadedFile(AiVoiceRecording $recording, UploadedFile $file): array
    {
        try {
            $recording->update(['status' => AiVoiceRecording::STATUS_PROCESSING]);

            // 임시 저장
            $tempPath = $file->store('temp', 'local');
            $fullPath = storage_path('app/'.$tempPath);

            // 파일 크기로 대략적인 재생 시간 추정 (12KB/초 기준)
            $fileSize = $file->getSize();
            $estimatedDuration = max(1, intval($fileSize / 12000));
            $recording->update(['duration_seconds' => $estimatedDuration]);

            // 1. GCS에 오디오 업로드
            $extension = $file->getClientOriginalExtension() ?: 'webm';
            $objectName = sprintf(
                'voice-recordings/%d/%d/%s.%s',
                $recording->tenant_id,
                $recording->id,
                now()->format('YmdHis'),
                $extension
            );

            $uploadResult = $this->googleCloudService->uploadToStorage($fullPath, $objectName);

            if (! $uploadResult) {
                @unlink($fullPath);
                throw new \Exception('오디오 파일 업로드 실패');
            }

            $gcsUri = $uploadResult['uri'];
            $uploadedSize = $uploadResult['size'] ?? $fileSize;

            $recording->update([
                'audio_file_path' => $objectName,
                'audio_gcs_uri' => $gcsUri,
            ]);

            // GCS 업로드 토큰 사용량 기록
            AiTokenHelper::saveGcsStorageUsage('AI음성녹음-GCS저장', $uploadedSize);

            // 2. Speech-to-Text 변환
            $transcript = $this->googleCloudService->speechToText($gcsUri);

            // 임시 파일 삭제
            @unlink($fullPath);

            if (! $transcript) {
                throw new \Exception('음성 인식 실패');
            }

            // STT 토큰 사용량 기록
            AiTokenHelper::saveSttUsage('AI음성녹음-STT변환', $estimatedDuration);

            $recording->update(['transcript_text' => $transcript]);

            // 3. Gemini AI 분석
            $analysis = $this->analyzeWithGemini($transcript);

            $recording->update([
                'analysis_text' => $analysis,
                'status' => AiVoiceRecording::STATUS_COMPLETED,
            ]);

            return [
                'ok' => true,
                'recording' => $recording->fresh(),
            ];
        } catch (\Exception $e) {
            Log::error('AiVoiceRecording 파일 처리 실패', [
                'recording_id' => $recording->id,
                'error' => $e->getMessage(),
            ]);

            $recording->update(['status' => AiVoiceRecording::STATUS_FAILED]);

            return [
                'ok' => false,
                'error' => $e->getMessage(),
            ];
        }
    }

    /**
     * Gemini API로 영업 시나리오 분석
     */
    private function analyzeWithGemini(string $transcript): ?string
    {
        $config = AiConfig::getActiveGemini();

        if (! $config) {
            Log::warning('Gemini API 설정이 없습니다.');

            return null;
        }

        $prompt = $this->buildAnalysisPrompt($transcript);

        if ($config->isVertexAi()) {
            return $this->callVertexAiApi($config, $prompt);
        }

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

    /**
     * Google AI Studio API 호출
     */
    private function callGoogleAiStudioApi(AiConfig $config, string $prompt): ?string
    {
        $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, $prompt, [
            'Content-Type' => 'application/json',
        ], false);
    }

    /**
     * Vertex AI API 호출
     */
    private function callVertexAiApi(AiConfig $config, string $prompt): ?string
    {
        $model = $config->model;
        $projectId = $config->getProjectId();
        $region = $config->getRegion();

        if (! $projectId) {
            Log::error('Vertex AI 프로젝트 ID가 설정되지 않았습니다.');

            return null;
        }

        $accessToken = $this->getAccessToken($config);
        if (! $accessToken) {
            Log::error('Google Cloud 인증 실패');

            return null;
        }

        $url = "https://{$region}-aiplatform.googleapis.com/v1/projects/{$projectId}/locations/{$region}/publishers/google/models/{$model}:generateContent";

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

    /**
     * Gemini API 공통 호출 로직
     */
    private function callGeminiApi(string $url, string $prompt, array $headers, bool $isVertexAi = false): ?string
    {
        $content = [
            'parts' => [
                ['text' => $prompt],
            ],
        ];

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

        try {
            $response = Http::timeout(120)
                ->withHeaders($headers)
                ->post($url, [
                    'contents' => [$content],
                    'generationConfig' => [
                        'temperature' => 0.3,
                        'topK' => 40,
                        'topP' => 0.95,
                        'maxOutputTokens' => 4096,
                    ],
                ]);

            if (! $response->successful()) {
                Log::error('Gemini API error', [
                    'status' => $response->status(),
                    'body' => $response->body(),
                ]);

                return null;
            }

            $result = $response->json();

            // 토큰 사용량 저장
            AiTokenHelper::saveGeminiUsage($result, $result['modelVersion'] ?? 'gemini', 'AI음성녹음분석');

            return $result['candidates'][0]['content']['parts'][0]['text'] ?? null;
        } catch (\Exception $e) {
            Log::error('Gemini API 예외', ['error' => $e->getMessage()]);

            return null;
        }
    }

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

        $possiblePaths = array_filter([
            $configuredPath,
            '/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;
        }

        $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);

        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()) {
                return $response->json()['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), '+/', '-_'), '=');
    }

    /**
     * 영업 시나리오 분석 프롬프트
     */
    private function buildAnalysisPrompt(string $transcript): string
    {
        return <<<PROMPT
다음은 회의 또는 영업 통화 녹취록을 텍스트로 변환한 내용입니다. 이 내용을 분석하여 구조화된 피드백을 제공해주세요.

## 분석 요청사항:

### 1. 전체 요약
- 녹취록의 핵심 내용을 3-5문장으로 요약해주세요.

### 2. 영업 강점 분석
- 잘한 점, 효과적인 커뮤니케이션, 성공적인 전략 등을 구체적으로 분석해주세요.

### 3. 개선이 필요한 부분
- 놓친 기회, 개선할 수 있는 표현, 더 효과적인 접근 방법 등을 제안해주세요.

### 4. 고객 반응/니즈 파악
- 고객이 표현한 관심사, 우려사항, 요구사항을 정리해주세요.
- 숨겨진 니즈나 잠재적 기회를 파악해주세요.

### 5. 다음 단계 제안
- 후속 조치 사항을 구체적으로 제안해주세요.
- 우선순위와 시기를 포함해주세요.

## 녹취록:
{$transcript}

## 분석 결과:
PROMPT;
    }

    /**
     * 삭제
     */
    public function delete(AiVoiceRecording $recording): bool
    {
        if ($recording->audio_file_path) {
            $this->googleCloudService->deleteFromStorage($recording->audio_file_path);
        }

        return $recording->delete();
    }

    /**
     * 만료된 파일 정리 (Cron용)
     */
    public function cleanupExpiredFiles(): int
    {
        $expired = AiVoiceRecording::where('file_expiry_date', '<=', now())
            ->whereNotNull('audio_file_path')
            ->get();

        $count = 0;
        foreach ($expired as $recording) {
            if ($recording->audio_file_path) {
                $this->googleCloudService->deleteFromStorage($recording->audio_file_path);
                $recording->update([
                    'audio_file_path' => null,
                    'audio_gcs_uri' => null,
                ]);
                $count++;
            }
        }

        return $count;
    }
}
