<?php

namespace App\Services;

use App\Models\Tenants\AiPricingConfig;
use App\Models\Tenants\AiReport;
use App\Models\Tenants\AiTokenUsage;
use App\Models\Tenants\Card;
use App\Models\Tenants\Deposit;
use App\Models\Tenants\Purchase;
use App\Models\Tenants\Sale;
use App\Models\Tenants\Withdrawal;
use Carbon\Carbon;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class AiReportService extends Service
{
    /**
     * AI 리포트 목록 조회
     */
    public function list(array $params): LengthAwarePaginator
    {
        $tenantId = $this->tenantId();
        $perPage = $params['per_page'] ?? 15;

        $query = AiReport::query()
            ->where('tenant_id', $tenantId)
            ->orderByDesc('report_date')
            ->orderByDesc('created_at');

        // 리포트 유형 필터
        if (! empty($params['report_type'])) {
            $query->where('report_type', $params['report_type']);
        }

        // 상태 필터
        if (! empty($params['status'])) {
            $query->where('status', $params['status']);
        }

        // 날짜 범위 필터
        if (! empty($params['start_date'])) {
            $query->whereDate('report_date', '>=', $params['start_date']);
        }
        if (! empty($params['end_date'])) {
            $query->whereDate('report_date', '<=', $params['end_date']);
        }

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

    /**
     * AI 리포트 상세 조회
     */
    public function show(int $id): AiReport
    {
        $tenantId = $this->tenantId();

        return AiReport::query()
            ->where('tenant_id', $tenantId)
            ->findOrFail($id);
    }

    /**
     * AI 리포트 생성
     */
    public function generate(array $params): AiReport
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();
        $reportDate = Carbon::parse($params['report_date'] ?? now()->toDateString());
        $reportType = $params['report_type'] ?? 'daily';

        // 비즈니스 데이터 수집
        $inputData = $this->collectBusinessData($tenantId, $reportDate, $reportType);

        // AI 리포트 레코드 생성 (pending 상태)
        $report = AiReport::create([
            'tenant_id' => $tenantId,
            'report_date' => $reportDate,
            'report_type' => $reportType,
            'status' => 'pending',
            'input_data' => $inputData,
            'created_by' => $userId,
        ]);

        try {
            // Gemini API 호출
            $aiResponse = $this->callGeminiApi($inputData);

            // 결과 저장
            $report->update([
                'content' => $aiResponse['리포트'] ?? [],
                'summary' => $aiResponse['요약'] ?? '',
                'status' => 'completed',
            ]);
        } catch (\Exception $e) {
            Log::error('AI Report generation failed', [
                'report_id' => $report->id,
                'error' => $e->getMessage(),
            ]);

            $report->update([
                'status' => 'failed',
                'error_message' => $e->getMessage(),
            ]);
        }

        return $report->fresh();
    }

    /**
     * AI 리포트 삭제
     */
    public function delete(int $id): bool
    {
        $tenantId = $this->tenantId();

        $report = AiReport::query()
            ->where('tenant_id', $tenantId)
            ->findOrFail($id);

        return $report->delete();
    }

    /**
     * 비즈니스 데이터 수집
     */
    private function collectBusinessData(int $tenantId, Carbon $reportDate, string $reportType): array
    {
        $startDate = $this->getStartDate($reportDate, $reportType);
        $endDate = $reportDate;

        // 전월 동기간 계산
        $prevStartDate = $startDate->copy()->subMonth();
        $prevEndDate = $endDate->copy()->subMonth();

        return [
            'report_date' => $reportDate->toDateString(),
            'report_type' => $reportType,
            'period' => [
                'start' => $startDate->toDateString(),
                'end' => $endDate->toDateString(),
            ],
            'expense' => $this->getExpenseData($tenantId, $startDate, $endDate, $prevStartDate, $prevEndDate),
            'sales' => $this->getSalesData($tenantId, $startDate, $endDate, $prevStartDate, $prevEndDate),
            'purchase' => $this->getPurchaseData($tenantId, $startDate, $endDate, $prevStartDate, $prevEndDate),
            'deposit_withdrawal' => $this->getDepositWithdrawalData($tenantId, $startDate, $endDate),
            'card_account' => $this->getCardAccountData($tenantId),
            'receivable' => $this->getReceivableData($tenantId, $reportDate),
        ];
    }

    /**
     * 리포트 유형별 시작일 계산
     */
    private function getStartDate(Carbon $reportDate, string $reportType): Carbon
    {
        return match ($reportType) {
            'weekly' => $reportDate->copy()->subDays(7),
            'monthly' => $reportDate->copy()->startOfMonth(),
            default => $reportDate->copy()->startOfDay(), // daily
        };
    }

    /**
     * 지출 데이터 수집
     */
    private function getExpenseData(int $tenantId, Carbon $start, Carbon $end, Carbon $prevStart, Carbon $prevEnd): array
    {
        $currentTotal = Withdrawal::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('withdrawal_date', [$start, $end])
            ->sum('amount');

        $prevTotal = Withdrawal::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('withdrawal_date', [$prevStart, $prevEnd])
            ->sum('amount');

        $changeRate = $prevTotal > 0 ? (($currentTotal - $prevTotal) / $prevTotal) * 100 : 0;

        return [
            'current_total' => (float) $currentTotal,
            'previous_total' => (float) $prevTotal,
            'change_rate' => round($changeRate, 1),
        ];
    }

    /**
     * 매출 데이터 수집
     */
    private function getSalesData(int $tenantId, Carbon $start, Carbon $end, Carbon $prevStart, Carbon $prevEnd): array
    {
        $currentTotal = Sale::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('sale_date', [$start, $end])
            ->sum('total_amount');

        $prevTotal = Sale::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('sale_date', [$prevStart, $prevEnd])
            ->sum('total_amount');

        $changeRate = $prevTotal > 0 ? (($currentTotal - $prevTotal) / $prevTotal) * 100 : 0;

        return [
            'current_total' => (float) $currentTotal,
            'previous_total' => (float) $prevTotal,
            'change_rate' => round($changeRate, 1),
        ];
    }

    /**
     * 매입 데이터 수집
     */
    private function getPurchaseData(int $tenantId, Carbon $start, Carbon $end, Carbon $prevStart, Carbon $prevEnd): array
    {
        $currentTotal = Purchase::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('purchase_date', [$start, $end])
            ->sum('total_amount');

        $prevTotal = Purchase::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('purchase_date', [$prevStart, $prevEnd])
            ->sum('total_amount');

        $changeRate = $prevTotal > 0 ? (($currentTotal - $prevTotal) / $prevTotal) * 100 : 0;

        return [
            'current_total' => (float) $currentTotal,
            'previous_total' => (float) $prevTotal,
            'change_rate' => round($changeRate, 1),
        ];
    }

    /**
     * 입출금 데이터 수집
     */
    private function getDepositWithdrawalData(int $tenantId, Carbon $start, Carbon $end): array
    {
        $totalDeposit = Deposit::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('deposit_date', [$start, $end])
            ->sum('amount');

        $totalWithdrawal = Withdrawal::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('withdrawal_date', [$start, $end])
            ->sum('amount');

        return [
            'total_deposit' => (float) $totalDeposit,
            'total_withdrawal' => (float) $totalWithdrawal,
            'net_flow' => (float) ($totalDeposit - $totalWithdrawal),
        ];
    }

    /**
     * 카드/계좌 데이터 수집
     */
    private function getCardAccountData(int $tenantId): array
    {
        $activeCards = Card::query()
            ->where('tenant_id', $tenantId)
            ->where('status', 'active')
            ->count();

        // 계좌 잔액은 입출금 내역 기반으로 계산
        $totalDeposits = Deposit::query()
            ->where('tenant_id', $tenantId)
            ->sum('amount');

        $totalWithdrawals = Withdrawal::query()
            ->where('tenant_id', $tenantId)
            ->sum('amount');

        $balance = $totalDeposits - $totalWithdrawals;

        return [
            'active_cards' => $activeCards,
            'current_balance' => (float) $balance,
        ];
    }

    /**
     * 미수금 데이터 수집
     */
    private function getReceivableData(int $tenantId, Carbon $reportDate): array
    {
        // 미결제 매출 (미수금)
        $receivables = Sale::query()
            ->where('tenant_id', $tenantId)
            ->whereIn('status', ['draft', 'confirmed'])
            ->whereNull('deposit_id')
            ->get();

        $totalReceivable = $receivables->sum('total_amount');
        $count = $receivables->count();

        // 연체 미수금 (30일 이상)
        $overdueDate = $reportDate->copy()->subDays(30);
        $overdueReceivables = $receivables->filter(function ($sale) use ($overdueDate) {
            return $sale->sale_date <= $overdueDate;
        });

        return [
            'total_amount' => (float) $totalReceivable,
            'count' => $count,
            'overdue_amount' => (float) $overdueReceivables->sum('total_amount'),
            'overdue_count' => $overdueReceivables->count(),
        ];
    }

    /**
     * Gemini API 호출
     */
    private function callGeminiApi(array $inputData): array
    {
        $apiKey = config('services.gemini.api_key');
        $model = config('services.gemini.model', 'gemini-2.5-flash');
        $baseUrl = config('services.gemini.base_url');

        if (empty($apiKey)) {
            throw new \RuntimeException(__('error.ai_report.api_key_not_configured'));
        }

        $prompt = $this->buildPrompt($inputData);

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

        try {
            $response = Http::timeout(30)
                ->post($url, [
                    'contents' => [
                        [
                            'parts' => [
                                ['text' => $prompt],
                            ],
                        ],
                    ],
                    'generationConfig' => [
                        'temperature' => 0.7,
                        'topK' => 40,
                        'topP' => 0.95,
                        'maxOutputTokens' => 2048,
                        'responseMimeType' => 'application/json',
                    ],
                ]);

            if (! $response->successful()) {
                Log::error('Gemini API error', [
                    'status' => $response->status(),
                    'body' => $response->body(),
                ]);
                throw new \RuntimeException(__('error.ai_report.api_call_failed'));
            }

            $result = $response->json();
            $text = $result['candidates'][0]['content']['parts'][0]['text'] ?? '';

            // 토큰 사용량 저장
            $this->saveTokenUsage($result, $model, 'AI리포트');

            // JSON 파싱
            $parsed = json_decode($text, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                Log::warning('AI response JSON parse failed', ['text' => $text]);

                return [
                    '리포트' => [],
                    '요약' => $text,
                ];
            }

            return $parsed;
        } catch (ConnectionException $e) {
            throw new \RuntimeException(__('error.ai_report.connection_failed'));
        }
    }

    /**
     * 토큰 사용량 저장
     */
    private function saveTokenUsage(array $apiResult, string $model, string $menuName): void
    {
        try {
            $usage = $apiResult['usageMetadata'] ?? null;
            if (! $usage) {
                return;
            }

            $promptTokens = $usage['promptTokenCount'] ?? 0;
            $completionTokens = $usage['candidatesTokenCount'] ?? 0;
            $totalTokens = $usage['totalTokenCount'] ?? 0;

            // DB 단가 조회 (fallback: 하드코딩 기본값)
            $pricing = AiPricingConfig::getActivePricing('gemini');
            $inputPricePerToken = $pricing ? (float) $pricing->input_price_per_million / 1_000_000 : 0.10 / 1_000_000;
            $outputPricePerToken = $pricing ? (float) $pricing->output_price_per_million / 1_000_000 : 0.40 / 1_000_000;

            $costUsd = ($promptTokens * $inputPricePerToken) + ($completionTokens * $outputPricePerToken);
            $exchangeRate = AiPricingConfig::getExchangeRate();
            $costKrw = $costUsd * $exchangeRate;

            AiTokenUsage::create([
                'tenant_id' => $this->tenantId(),
                'model' => $model,
                'menu_name' => $menuName,
                'prompt_tokens' => $promptTokens,
                'completion_tokens' => $completionTokens,
                'total_tokens' => $totalTokens,
                'cost_usd' => $costUsd,
                'cost_krw' => $costKrw,
                'request_id' => Str::uuid()->toString(),
                'created_by' => $this->apiUserId(),
            ]);
        } catch (\Exception $e) {
            Log::warning('AI token usage save failed', ['error' => $e->getMessage()]);
        }
    }

    /**
     * AI 프롬프트 생성
     */
    private function buildPrompt(array $inputData): string
    {
        $dataJson = json_encode($inputData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

        return <<<PROMPT
당신은 기업 재무 분석 전문가입니다. 아래 비즈니스 데이터를 분석하여 경영진을 위한 리포트를 생성해주세요.

## 작성 규칙
1. 문장은 간결하고 명확하게 작성
2. 숫자는 읽기 쉽게 "3,123,000원", "15%" 형식 사용
3. 계정과목명, 거래처명은 구체적으로 명시
4. 조치가 필요한 경우 구체적인 행동 권한 포함
5. 긍정적 변화도 반드시 실상 포함
6. 법인세, 소득세 영향이 있는 경우 세무 리스크 명시

## 상태 코드
- "경고" (critical): 즉시 조치 필요 (빨간색)
- "주의" (warning): 확인/점검 필요 (주황색)
- "긍정" (positive): 긍정적 변화 (녹색)
- "양호" (normal): 정상 상태 (파란색)

## 분석 영역
- 지출분석: 지출 증감, 비용 구조
- 가지급금: 미정산 항목, 인정이자 리스크
- 카드/계좌: 한도, 잔액 상태
- 미수금: 연체 현황, 회수 필요 항목
- 매출분석: 매출 추이, 거래처별 현황
- 매입분석: 매입 추이, 비용 증감

## 입력 데이터
{$dataJson}

## 출력 형식 (JSON)
{
  "리포트": [
    {"영역": "지출분석", "상태": "경고|주의|긍정|양호", "메시지": "핵심 메시지", "상세": "상세 설명"},
    ...
  ],
  "요약": "전체 요약 메시지 (1-2문장)"
}

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