<?php

namespace App\Services\Machine;

use App\Models\Machine\Machine;
use App\Models\Machine\MachineLog;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class MachineService
{
    /**
     * Update machine heartbeat and status.
     * 
     * @param string $serialNo
     * @param array $data
     * @return Machine
     */
    public function updateHeartbeat(string $serialNo, array $data): Machine
    {
        return DB::transaction(function () use ($serialNo, $data) {
            $machine = Machine::where('serial_no', $serialNo)->firstOrFail();

            // 採用現代化語意命名 (Modern semantic naming)
            $temperature = $data['temperature'] ?? $machine->temperature;
            $currentPage = $data['current_page'] ?? $machine->current_page;
            $doorStatus = $data['door_status'] ?? $machine->door_status;
            $firmwareVersion = $data['firmware_version'] ?? $machine->firmware_version;
            $model = $data['model'] ?? $machine->model;

            $updateData = [
                'status' => 'online',
                'temperature' => $temperature,
                'current_page' => $currentPage,
                'door_status' => $doorStatus,
                'firmware_version' => $firmwareVersion,
                'model' => $model,
                'last_heartbeat_at' => now(),
            ];

            $machine->update($updateData);

            // Record log if provided
            if (!empty($data['log'])) {
                $machine->logs()->create([
                    'company_id' => $machine->company_id,
                    'type' => 'status',
                    'level' => $data['log_level'] ?? 'info',
                    'message' => $data['log'],
                    'context' => $data['log_payload'] ?? null,
                ]);
            }

            return $machine;
        });
    }

    /**
     * Sync machine slots based on replenishment report.
     * 
     * @param Machine $machine
     * @param array $slotsData
     */
    public function syncSlots(Machine $machine, array $slotsData): void
    {
        DB::transaction(function () use ($machine, $slotsData) {
            foreach ($slotsData as $slotData) {
                $slotNo = $slotData['slot_no'] ?? null;
                if (!$slotNo) continue;

                $existingSlot = $machine->slots()->where('slot_no', $slotNo)->first();

                $updateData = [
                    'product_id' => $slotData['product_id'] ?? null,
                    'stock' => $slotData['stock'] ?? 0,
                    'capacity' => $slotData['capacity'] ?? ($existingSlot->capacity ?? 10),
                    'price' => $slotData['price'] ?? ($existingSlot->price ?? 0),
                    'last_restocked_at' => now(),
                ];

                // 如果商品變了，或者這是一次明確的補貨回報，清空效期等待管理員更新
                // 這裡我們暫定只要有 report 進來，就需要重新確認效期
                $updateData['expiry_date'] = null;

                if ($existingSlot) {
                    $existingSlot->update($updateData);
                } else {
                    $machine->slots()->create(array_merge($updateData, ['slot_no' => $slotNo]));
                }
            }
        });
    }

    /**
     * Update machine slot stock (single slot).
     * Legacy support for recordLog (Existing code).
     */
    public function recordLog(int $machineId, array $data): MachineLog
    {
        $machine = Machine::findOrFail($machineId);

        return $machine->logs()->create([
            'level' => $data['level'] ?? 'info',
            'message' => $data['message'],
            'context' => $data['context'] ?? null,
        ]);
    }

    /**
     * Get machine utilization and OEE statistics for entire fleet.
     */
    public function getFleetStats(string $date): array
    {
        $start = Carbon::parse($date)->startOfDay();
        $end = Carbon::parse($date)->endOfDay();

        // 1. Online Count (Base on current status)
        $machines = Machine::all(); // This is filtered by TenantScoped
        $totalMachines = $machines->count();
        $onlineCount = $machines->where('status', 'online')->count();

        $machineIds = $machines->pluck('id')->toArray();

        // 2. Total Daily Sales (Sum of B600 logs across all authorized machines)
        $totalSales = MachineLog::whereIn('machine_id', $machineIds)
            ->where('message', 'like', '%B600%')
            ->whereBetween('created_at', [$start, $end])
            ->count();

        // 3. Average OEE (Simulated based on individual machine stats for performance)
        $totalOee = 0;
        $count = 0;
        
        foreach ($machines as $machine) {
            $stats = $this->getUtilizationStats($machine, $date);
            $totalOee += $stats['overview']['oee'];
            $count++;
        }

        $avgOee = ($count > 0) ? ($totalOee / $count) : 0;

        return [
            'avgOee' => round($avgOee, 2),
            'onlineCount' => $onlineCount,
            'totalMachines' => $totalMachines,
            'totalSales' => $totalSales,
            'alertCount' => MachineLog::whereIn('machine_id', $machineIds)
                ->where('level', 'error')
                ->whereBetween('created_at', [$start, $end])
                ->count()
        ];
    }

    /**
     * Get machine utilization and OEE statistics.
     */
    public function getUtilizationStats(Machine $machine, string $date): array
    {
        $start = Carbon::parse($date)->startOfDay();
        $end = Carbon::parse($date)->endOfDay();

        // 1. Availability: Based on heartbeat logs (status type)
        // Assume online if heartbeat within 6 minutes
        $logs = $machine->logs()
            ->where('type', 'status')
            ->whereBetween('created_at', [$start, $end])
            ->orderBy('created_at')
            ->get();

        $onlineMinutes = 0;
        $lastLogTime = null;

        foreach ($logs as $log) {
            $currentTime = Carbon::parse($log->created_at);
            if ($lastLogTime) {
                $diff = $currentTime->diffInMinutes($lastLogTime);
                if ($diff <= 6) {
                    $onlineMinutes += $diff;
                }
            }
            $lastLogTime = $currentTime;
        }

        $totalMinutes = 24 * 60;
        $availability = ($totalMinutes > 0) ? min(100, ($onlineMinutes / $totalMinutes) * 100) : 0;

        // 2. Performance: Sales Count (B600)
        // Target: 2 sales per hour (48/day)
        $salesCount = $machine->logs()
            ->where('message', 'like', '%B600%')
            ->whereBetween('created_at', [$start, $end])
            ->count();
        
        $targetSales = 48;
        $performance = ($targetSales > 0) ? min(100, ($salesCount / $targetSales) * 100) : 0;

        // 3. Quality: Success Rate
        // Exclude failed dispense (B130)
        $errorCount = $machine->logs()
            ->where('message', 'like', '%B130%')
            ->whereBetween('created_at', [$start, $end])
            ->count();
        
        $totalAttempts = $salesCount + $errorCount;
        $quality = ($totalAttempts > 0) ? (($salesCount / $totalAttempts) * 100) : 100;

        // Combined OEE
        $oee = ($availability / 100) * ($performance / 100) * ($quality / 100) * 100;

        return [
            'overview' => [
                'availability' => round($availability, 2),
                'performance' => round($performance, 2),
                'quality' => round($quality, 2),
                'oee' => round($oee, 2),
                'onlineHours' => round($onlineMinutes / 60, 2),
                'salesCount' => $salesCount,
                'errorCount' => $errorCount,
            ],
            'chart' => [
                'uptime' => $this->formatUptimeTimeline($logs, $start, $end),
                'sales' => $this->formatSalesTimeline($machine, $start, $end)
            ]
        ];
    }

    private function formatUptimeTimeline($logs, $start, $end)
    {
        $data = [];
        if ($logs->isEmpty()) return $data;

        $lastLog = null;
        $currentRangeStart = null;

        foreach ($logs as $log) {
            $logTime = Carbon::parse($log->created_at);
            if (!$currentRangeStart) {
                $currentRangeStart = $logTime;
            } else {
                $diff = $logTime->diffInMinutes(Carbon::parse($lastLog->created_at));
                if ($diff > 10) { // Interruption > 10 mins
                    $data[] = [
                        'x' => 'Uptime',
                        'y' => [$currentRangeStart->getTimestamp() * 1000, Carbon::parse($lastLog->created_at)->getTimestamp() * 1000],
                        'fillColor' => '#06b6d4'
                    ];
                    $currentRangeStart = $logTime;
                }
            }
            $lastLog = $log;
        }

        if ($currentRangeStart && $lastLog) {
            $data[] = [
                'x' => 'Uptime',
                'y' => [$currentRangeStart->getTimestamp() * 1000, Carbon::parse($lastLog->created_at)->getTimestamp() * 1000],
                'fillColor' => '#06b6d4'
            ];
        }

        return $data;
    }

    private function formatSalesTimeline($machine, $start, $end)
    {
        return $machine->logs()
            ->where('message', 'like', '%B600%')
            ->whereBetween('created_at', [$start, $end])
            ->get()
            ->map(function($log) {
                return [Carbon::parse($log->created_at)->getTimestamp() * 1000, 1];
            })
            ->toArray();
    }
}
