<?php

namespace App\Services;

use App\Models\Stats\StatAlert;
use App\Models\Stats\StatFinanceDaily;
use App\Models\Stats\StatFinanceMonthly;
use App\Models\Stats\StatInventoryDaily;
use App\Models\Stats\StatProductionDaily;
use App\Models\Stats\StatSalesDaily;
use App\Models\Stats\StatSalesMonthly;
use App\Models\Stats\StatSystemDaily;
use Illuminate\Support\Carbon;

class DashboardStatService
{
    /**
     * 요약 카드 데이터 (오늘 + 어제)
     */
    public function getSummaryCards(?int $tenantId): array
    {
        $today = Carbon::today()->toDateString();
        $yesterday = Carbon::yesterday()->toDateString();

        $salesToday = $this->getDailySales($tenantId, $today);
        $salesYesterday = $this->getDailySales($tenantId, $yesterday);

        $financeToday = $this->getDailyFinance($tenantId, $today);
        $financeYesterday = $this->getDailyFinance($tenantId, $yesterday);

        $productionToday = $this->getDailyProduction($tenantId, $today);
        $productionYesterday = $this->getDailyProduction($tenantId, $yesterday);

        $inventoryToday = $this->getDailyInventory($tenantId, $today);
        $inventoryYesterday = $this->getDailyInventory($tenantId, $yesterday);

        $systemToday = $this->getDailySystem($tenantId, $today);
        $systemYesterday = $this->getDailySystem($tenantId, $yesterday);

        return [
            'orders' => [
                'count' => $salesToday['order_count'],
                'amount' => $salesToday['order_amount'],
                'delta_count' => $salesToday['order_count'] - $salesYesterday['order_count'],
                'delta_amount' => $salesToday['order_amount'] - $salesYesterday['order_amount'],
            ],
            'sales' => [
                'amount' => $salesToday['sales_amount'],
                'delta' => $salesToday['sales_amount'] - $salesYesterday['sales_amount'],
            ],
            'cashflow' => [
                'net' => $financeToday['net_cashflow'],
                'delta' => $financeToday['net_cashflow'] - $financeYesterday['net_cashflow'],
            ],
            'production' => [
                'efficiency' => $productionToday['efficiency_rate'],
                'defect_rate' => $productionToday['defect_rate'],
                'delta_efficiency' => $productionToday['efficiency_rate'] - $productionYesterday['efficiency_rate'],
            ],
            'inventory' => [
                'below_safety' => $inventoryToday['below_safety_count'],
                'delta' => $inventoryToday['below_safety_count'] - $inventoryYesterday['below_safety_count'],
            ],
            'system' => [
                'active_users' => $systemToday['active_user_count'],
                'delta' => $systemToday['active_user_count'] - $systemYesterday['active_user_count'],
            ],
        ];
    }

    /**
     * 매출 추이 (7일)
     */
    public function getSalesTrend(?int $tenantId, int $days = 7): array
    {
        $from = Carbon::today()->subDays($days - 1)->toDateString();
        $to = Carbon::today()->toDateString();

        $rows = StatSalesDaily::forTenant($tenantId)
            ->forDateRange($from, $to)
            ->selectRaw('stat_date, SUM(order_amount) as order_amount, SUM(sales_amount) as sales_amount')
            ->groupBy('stat_date')
            ->orderBy('stat_date')
            ->get();

        return $this->fillDates($this->generateDateRange($from, $to), $rows, ['order_amount', 'sales_amount']);
    }

    /**
     * 자금 흐름 추이 (7일)
     */
    public function getFinanceTrend(?int $tenantId, int $days = 7): array
    {
        $from = Carbon::today()->subDays($days - 1)->toDateString();
        $to = Carbon::today()->toDateString();

        $rows = StatFinanceDaily::forTenant($tenantId)
            ->forDateRange($from, $to)
            ->selectRaw('stat_date, SUM(deposit_amount) as deposit_amount, SUM(withdrawal_amount) as withdrawal_amount')
            ->groupBy('stat_date')
            ->orderBy('stat_date')
            ->get();

        return $this->fillDates($this->generateDateRange($from, $to), $rows, ['deposit_amount', 'withdrawal_amount']);
    }

    /**
     * 생산 현황 추이 (7일)
     */
    public function getProductionTrend(?int $tenantId, int $days = 7): array
    {
        $from = Carbon::today()->subDays($days - 1)->toDateString();
        $to = Carbon::today()->toDateString();

        $rows = StatProductionDaily::forTenant($tenantId)
            ->forDateRange($from, $to)
            ->selectRaw('stat_date, SUM(production_qty) as production_qty, AVG(defect_rate) as defect_rate')
            ->groupBy('stat_date')
            ->orderBy('stat_date')
            ->get();

        return $this->fillDates($this->generateDateRange($from, $to), $rows, ['production_qty', 'defect_rate']);
    }

    /**
     * 시스템 활동 추이 (7일)
     */
    public function getSystemTrend(?int $tenantId, int $days = 7): array
    {
        $from = Carbon::today()->subDays($days - 1)->toDateString();
        $to = Carbon::today()->toDateString();

        $rows = StatSystemDaily::forTenant($tenantId)
            ->forDateRange($from, $to)
            ->selectRaw('stat_date, SUM(active_user_count) as active_user_count, SUM(api_request_count) as api_request_count')
            ->groupBy('stat_date')
            ->orderBy('stat_date')
            ->get();

        return $this->fillDates($this->generateDateRange($from, $to), $rows, ['active_user_count', 'api_request_count']);
    }

    /**
     * 최근 알림 5건
     */
    public function getRecentAlerts(?int $tenantId, int $limit = 5): \Illuminate\Database\Eloquent\Collection
    {
        $query = StatAlert::query()
            ->where('is_resolved', false)
            ->orderByDesc('created_at')
            ->limit($limit);

        if ($tenantId) {
            $query->where('tenant_id', $tenantId);
        }

        return $query->get();
    }

    /**
     * 이번 달 도메인별 요약
     */
    public function getMonthlySummary(?int $tenantId): array
    {
        $year = (int) Carbon::now()->format('Y');
        $month = (int) Carbon::now()->format('n');
        $monthLabel = Carbon::now()->format('Y-m');

        $sales = StatSalesMonthly::forTenant($tenantId)
            ->forMonth($year, $month)
            ->selectRaw('SUM(order_count) as order_count, SUM(order_amount) as order_amount, SUM(sales_amount) as sales_amount')
            ->first();

        $finance = StatFinanceMonthly::forTenant($tenantId)
            ->forMonth($year, $month)
            ->selectRaw('SUM(deposit_total) as deposit_total, SUM(withdrawal_total) as withdrawal_total, SUM(net_cashflow) as net_cashflow')
            ->first();

        return [
            'month' => $monthLabel,
            'sales' => [
                'order_count' => (int) ($sales->order_count ?? 0),
                'order_amount' => (float) ($sales->order_amount ?? 0),
                'sales_amount' => (float) ($sales->sales_amount ?? 0),
            ],
            'finance' => [
                'deposit_total' => (float) ($finance->deposit_total ?? 0),
                'withdrawal_total' => (float) ($finance->withdrawal_total ?? 0),
                'net_cashflow' => (float) ($finance->net_cashflow ?? 0),
            ],
        ];
    }

    // --- Private Helpers ---

    private function getDailySales(?int $tenantId, string $date): array
    {
        $row = StatSalesDaily::forTenant($tenantId)
            ->forDate($date)
            ->selectRaw('SUM(order_count) as order_count, SUM(order_amount) as order_amount, SUM(sales_amount) as sales_amount')
            ->first();

        return [
            'order_count' => (int) ($row->order_count ?? 0),
            'order_amount' => (float) ($row->order_amount ?? 0),
            'sales_amount' => (float) ($row->sales_amount ?? 0),
        ];
    }

    private function getDailyFinance(?int $tenantId, string $date): array
    {
        $row = StatFinanceDaily::forTenant($tenantId)
            ->forDate($date)
            ->selectRaw('SUM(net_cashflow) as net_cashflow, SUM(deposit_amount) as deposit_amount, SUM(withdrawal_amount) as withdrawal_amount')
            ->first();

        return [
            'net_cashflow' => (float) ($row->net_cashflow ?? 0),
            'deposit_amount' => (float) ($row->deposit_amount ?? 0),
            'withdrawal_amount' => (float) ($row->withdrawal_amount ?? 0),
        ];
    }

    private function getDailyProduction(?int $tenantId, string $date): array
    {
        $row = StatProductionDaily::forTenant($tenantId)
            ->forDate($date)
            ->selectRaw('AVG(efficiency_rate) as efficiency_rate, AVG(defect_rate) as defect_rate')
            ->first();

        return [
            'efficiency_rate' => (float) ($row->efficiency_rate ?? 0),
            'defect_rate' => (float) ($row->defect_rate ?? 0),
        ];
    }

    private function getDailyInventory(?int $tenantId, string $date): array
    {
        $row = StatInventoryDaily::forTenant($tenantId)
            ->forDate($date)
            ->selectRaw('SUM(below_safety_count) as below_safety_count')
            ->first();

        return [
            'below_safety_count' => (int) ($row->below_safety_count ?? 0),
        ];
    }

    private function getDailySystem(?int $tenantId, string $date): array
    {
        $row = StatSystemDaily::forTenant($tenantId)
            ->forDate($date)
            ->selectRaw('SUM(active_user_count) as active_user_count')
            ->first();

        return [
            'active_user_count' => (int) ($row->active_user_count ?? 0),
        ];
    }

    private function generateDateRange(string $from, string $to): array
    {
        $dates = [];
        $current = Carbon::parse($from);
        $end = Carbon::parse($to);

        while ($current->lte($end)) {
            $dates[] = $current->toDateString();
            $current->addDay();
        }

        return $dates;
    }

    private function fillDates(array $dates, $rows, array $fields): array
    {
        $indexed = $rows->keyBy(fn ($row) => Carbon::parse($row->stat_date)->toDateString());

        $result = [
            'labels' => [],
            'datasets' => [],
        ];

        foreach ($fields as $field) {
            $result['datasets'][$field] = [];
        }

        foreach ($dates as $date) {
            $result['labels'][] = Carbon::parse($date)->format('m/d');
            foreach ($fields as $field) {
                $result['datasets'][$field][] = (float) ($indexed[$date]?->$field ?? 0);
            }
        }

        return $result;
    }
}
