<?php

namespace App\Http\Controllers;

use App\Models\JadwalBel;
use App\Models\Status;
use App\Models\BellHistory;
use App\Services\MqttService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;

class BelController extends Controller
{
    protected $mqttService;
    protected $mqttConfig;

    public function __construct(MqttService $mqttService)
    {
        $this->mqttService = $mqttService;
        $this->mqttConfig = config('mqtt');
        
    }

    protected function initializeMqttSubscriptions()
    {
        try {
            // Subscribe ke topik status response
            $this->mqttService->subscribe(
                $this->mqttConfig['topics']['responses']['status'],
                function (string $topic, string $message) {
                    $this->handleStatusResponse($message);
                }
            );

            // Subscribe ke topik acknowledgment
            $this->mqttService->subscribe(
                $this->mqttConfig['topics']['responses']['ack'],
                function (string $topic, string $message) {
                    $this->handleAckResponse($message);
                }
            );

            // Tambahkan subscribe untuk topik bell ring
            $this->mqttService->subscribe(
                $this->mqttConfig['topics']['responses']['bell_ring'],
                function (string $topic, string $message) {
                    $this->handleBellRing($message);
                }
            );
        } catch (\Exception $e) {
            Log::error('Failed to initialize MQTT subscriptions: ' . $e->getMessage());
        }
    }

    protected function handleStatusResponse(string $message)
    {
        try {
            $data = json_decode($message, true);
            // Validasi payload
            if (!is_array($data)) {
                Log::error('Invalid status data format');
                return;
            }
            // Pastikan semua kunci penting ada
            $requiredKeys = ['rtc', 'dfplayer', 'rtc_time', 'last_communication', 'last_sync'];
            foreach ($requiredKeys as $key) {
                if (!array_key_exists($key, $data)) {
                    Log::error("Missing required key in status data: {$key}");
                    return;
                }
            }
            // Simpan data ke database
            Status::updateOrCreate(
                ['id' => 1],
                [
                    'rtc' => $data['rtc'] ?? false,
                    'dfplayer' => $data['dfplayer'] ?? false,
                    'rtc_time' => $data['rtc_time'] ?? null,
                    'last_communication' => Carbon::createFromTimestamp($data['last_communication'] ?? 0),
                    'last_sync' => Carbon::createFromTimestamp($data['last_sync'] ?? 0)
                ]
            );
        } catch (\Exception $e) {
            Log::error('Error handling status response: ' . $e->getMessage());
        }
    }

    protected function handleAckResponse(string $message)
    {
        try {
            $data = json_decode($message, true);
            
            if (isset($data['action'])) {
                $action = $data['action'];
                $message = $data['message'] ?? '';
                
                if ($action === 'sync_ack') {
                    Status::updateOrCreate(
                        ['id' => 1],
                        ['last_sync' => Carbon::now()]
                    );
                    Log::info('Schedule sync acknowledged: ' . $message);
                } elseif ($action === 'ring_ack') {
                    Log::info('Bell ring acknowledged: ' . $message);
                }
            }
        } catch (\Exception $e) {
            Log::error('Error handling ack response: ' . $e->getMessage());
        }
    }

    public function index(Request $request)
    {
        try {
            // Ambil data utama terlepas dari koneksi MQTT
            $query = JadwalBel::query();
            
            if ($request->filled('hari')) {
                $query->where('hari', $request->hari);
            }
            
            if ($request->filled('search')) {
                $query->where(function($q) use ($request) {
                    $q->where('hari', 'like', '%'.$request->search.'%')
                      ->orWhere('file_number', 'like', '%'.$request->search.'%');
                });
            }
            
            $schedules = $query->orderBy('hari')->orderBy('waktu')->paginate(10);
            $status = Status::firstOrCreate(['id' => 1]);
            
            // Jadwal hari ini
            $todaySchedules = JadwalBel::where('hari', Carbon::now()->isoFormat('dddd'))
                ->orderBy('waktu')
                ->get();
                
            // Jadwal berikutnya
            $nextSchedule = JadwalBel::where('hari', Carbon::now()->isoFormat('dddd'))
                ->where('waktu', '>', Carbon::now()->format('H:i:s'))
                ->orderBy('waktu')
                ->first();
    
            // Cek koneksi MQTT tanpa menghentikan eksekusi jika error
            try {
                $mqttStatus = $this->mqttService->isConnected() ? 'Connected' : 'Disconnected';
            } catch (\Exception $e) {
                $mqttStatus = 'Disconnected';
                Log::error('MQTT check failed: ' . $e->getMessage());
            }
    
            return view('admin.bel.index', [
                'schedules' => $schedules,
                'todaySchedules' => $todaySchedules,
                'nextSchedule' => $nextSchedule,
                'status' => $status,
                'mqttStatus' => $mqttStatus
            ]);
            
        } catch (\Exception $e) {
            Log::error('Error in index method: ' . $e->getMessage());
            return back()->with('error', 'Terjadi kesalahan saat memuat data jadwal');
        }
    }


    public function create()
    {
        return view('admin.bel.create', [
            'days' => JadwalBel::DAYS,
            'default_file' => '0001'
        ]);
    }

    public function store(Request $request)
    {
        $validated = $this->validateSchedule($request);
        
        try {
            $schedule = JadwalBel::create($validated);
            
            $this->syncSchedules();
            $this->logActivity('Jadwal dibuat', $schedule);
            
            return redirect()
                ->route('bel.index')
                ->with([
                    'success' => 'Jadwal berhasil ditambahkan',
                    'scroll_to' => 'schedule-'.$schedule->id
                ]);
        } catch (\Exception $e) {
            Log::error('Gagal menambah jadwal: ' . $e->getMessage());
            return back()
                ->withInput()
                ->with('error', 'Gagal menambah jadwal: ' . $e->getMessage());
        }
    }

    public function edit($id)
    {
        $schedule = JadwalBel::findOrFail($id);
        
        return view('admin.bel.edit', [
            'schedule' => $schedule,
            'days' => JadwalBel::DAYS
        ]);
    }

    public function update(Request $request, $id)
    {
        $validated = $this->validateSchedule($request);
        $schedule = JadwalBel::findOrFail($id);
        
        try {
            $schedule->update($validated);
            
            $this->syncSchedules();
            $this->logActivity('Jadwal diperbarui', $schedule);
            
            return redirect()
                ->route('bel.index')
                ->with([
                    'success' => 'Jadwal berhasil diperbarui',
                    'scroll_to' => 'schedule-'.$schedule->id
                ]);
        } catch (\Exception $e) {
            Log::error('Gagal update jadwal ID '.$schedule->id.': ' . $e->getMessage());
            return back()
                ->withInput()
                ->with('error', 'Gagal memperbarui jadwal: ' . $e->getMessage());
        }
    }

    public function destroy($id)
    {
        $schedule = JadwalBel::findOrFail($id);
        
        try {
            $schedule->delete();
            
            $this->syncSchedules();
            $this->logActivity('Jadwal dihapus', $schedule);
            
            return redirect()
                ->route('bel.index')
                ->with('success', 'Jadwal berhasil dihapus');
        } catch (\Exception $e) {
            Log::error('Gagal hapus jadwal ID '.$schedule->id.': ' . $e->getMessage());
            return back()
                ->with('error', 'Gagal menghapus jadwal: ' . $e->getMessage());
        }
    }

    public function deleteAll()
    {
        try {
            JadwalBel::truncate();
            $this->syncSchedules();
            
            return redirect()
                ->route('bel.index')
                ->with('success', 'Semua jadwal berhasil dihapus');
        } catch (\Exception $e) {
            Log::error('Gagal hapus semua jadwal: ' . $e->getMessage());
            return back()
                ->with('error', 'Gagal menghapus semua jadwal: ' . $e->getMessage());
        }
    }

    public function toggleStatus($id)
    {
        try {
            $schedule = JadwalBel::findOrFail($id);
            $newStatus = !$schedule->is_active;
            $schedule->is_active = $newStatus;
            $schedule->save();
    
            // Return JSON response for AJAX requests
            if (request()->wantsJson()) {
                return response()->json([
                    'success' => true,
                    'message' => 'Status jadwal berhasil diubah',
                    'is_active' => $newStatus
                ]);
            }
    
            // Fallback for non-AJAX requests
            return redirect()->back()->with('success', 'Status jadwal berhasil diubah');
            
        } catch (\Exception $e) {
            if (request()->wantsJson()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Gagal mengubah status: ' . $e->getMessage()
                ], 500);
            }
    
            return redirect()->back()->with('error', 'Gagal mengubah status');
        }
    }

    public function activateAll()
    {
        try {
            JadwalBel::query()->update(['is_active' => true]);
            $this->syncSchedules();
            
            return response()->json([
                'success' => true,
                'message' => 'Semua jadwal diaktifkan'
            ]);
        } catch (\Exception $e) {
            Log::error('Gagal aktifkan semua jadwal: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Gagal mengaktifkan semua jadwal'
            ], 500);
        }
    }

    public function deactivateAll()
    {
        try {
            JadwalBel::query()->update(['is_active' => false]);
            $this->syncSchedules();
            
            return response()->json([
                'success' => true,
                'message' => 'Semua jadwal dinonaktifkan'
            ]);
        } catch (\Exception $e) {
            Log::error('Gagal nonaktifkan semua jadwal: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Gagal menonaktifkan semua jadwal'
            ], 500);
        }
    }

    public function ring(Request $request)
    {
        $validated = $request->validate([
            'file_number' => 'required|string|size:4',
            'repeat' => 'sometimes|integer|min:1|max:10',
            'volume' => 'sometimes|integer|min:0|max:30'
        ]);
        
        try {
            // Catat ke history terlebih dahulu
            BellHistory::create([
                'hari' => Carbon::now()->isoFormat('dddd'),
                'waktu' => Carbon::now()->format('H:i:s'),
                'file_number' => $validated['file_number'],
                'trigger_type' => BellHistory::TRIGGER_MANUAL,
                'ring_time' => Carbon::now(),
                'volume' => $validated['volume'] ?? 15,
                'repeat' => $validated['repeat'] ?? 1
            ]);

            // Kirim perintah ke MQTT
            $message = json_encode([
                'action' => 'ring',
                'timestamp' => Carbon::now()->toDateTimeString(),
                'file_number' => $validated['file_number'],
                'repeat' => $validated['repeat'] ?? 1,
                'volume' => $validated['volume'] ?? 15,
                'trigger_type' => BellHistory::TRIGGER_MANUAL
            ]);
            
            $this->mqttService->publish(
                $this->mqttConfig['topics']['commands']['ring'],
                $message,
                1
            );
            
            return response()->json([
                'success' => true,
                'message' => 'Perintah bel berhasil dikirim',
                'data' => [
                    'file_number' => $validated['file_number'],
                    'timestamp' => Carbon::now()->toDateTimeString()
                ]
            ]);
        } catch (\Exception $e) {
            Log::error('Gagal mengirim bel manual: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Gagal mengirim perintah bel: ' . $e->getMessage()
            ], 500);
        }
    }

    public function status()
    {
        try {
            $message = json_encode([
                'action' => 'get_status',
                'timestamp' => Carbon::now()->toDateTimeString()
            ]);
            $this->mqttService->publish(
                $this->mqttConfig['topics']['commands']['status'],
                $message,
                1
            );
            $status = Status::firstOrCreate(['id' => 1]);
            return response()->json([
                'success' => true,
                'message' => 'Permintaan status terkirim',
                'data' => [
                    'rtc' => $status->rtc,
                    'dfplayer' => $status->dfplayer,
                    'rtc_time' => $status->rtc_time,
                    'last_communication' => $status->last_communication,
                    'last_sync' => $status->last_sync,
                    'mqtt_status' => $this->mqttService->isConnected(),
                    'status' => $status->status ?? 'unknown' // Default value jika kolom kosong
                ]
            ]);
        } catch (\Exception $e) {
            Log::error('Gagal meminta status: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Gagal meminta status perangkat: ' . $e->getMessage()
            ], 500);
        }
    }

    protected function syncJadwalToEsp($schedules)
    {
        try {
            $message = json_encode([
                'action' => 'sync',
                'timestamp' => Carbon::now()->toDateTimeString(),
                'schedules' => $schedules
            ]);

            $this->mqttService->publish(
                $this->mqttConfig['topics']['commands']['sync'],
                $message,
                1
            );

            Log::info("Sync schedules sent to MQTT", ['count' => count($schedules)]);
        } catch (\Exception $e) {
            Log::error('Error syncing schedules to MQTT: ' . $e->getMessage());
        }
    }

    public function syncSchedule()
    {
        try {
            $schedules = JadwalBel::active()
                ->get()
                ->map(function ($item) {
                    return [
                        'hari' => $item->hari,
                        'waktu' => Carbon::parse($item->waktu)->format('H:i'), // Pastikan format waktu sesuai
                        'file_number' => $item->file_number
                    ];
                });

            $message = json_encode([
                'action' => 'sync',
                'timestamp' => Carbon::now()->toDateTimeString(),
                'schedules' => $schedules
            ]);

            Log::info("Sync message sent to MQTT", ['message' => $message]); // Debugging

            $this->mqttService->publish(
                $this->mqttConfig['topics']['commands']['sync'],
                $message,
                1
            );

            Status::updateOrCreate(
                ['id' => 1], 
                ['last_sync' => Carbon::now()]
            );

            return response()->json([
                'success' => true,
                'message' => 'Jadwal berhasil disinkronisasi',
                'data' => [
                    'count' => $schedules->count(),
                    'last_sync' => Carbon::now()->toDateTimeString()
                ]
            ]);
        } catch (\Exception $e) {
            Log::error('Gagal sync jadwal: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Gagal menyinkronisasi jadwal: ' . $e->getMessage()
            ], 500);
        }
    }

    protected function syncSchedules()
    {
        try {
            $schedules = JadwalBel::active()
                ->get()
                ->map(function ($item) {
                    return [
                        'hari' => $item->hari,
                        'waktu' => Carbon::parse($item->waktu)->format('H:i:s'),
                        'file_number' => $item->file_number
                    ];
                });

            $this->syncJadwalToEsp($schedules);

            Log::info("Auto sync: " . count($schedules) . " jadwal");
        } catch (\Exception $e) {
            Log::error('Gagal auto sync: ' . $e->getMessage());
        }
    }

    public function getNextSchedule()
    {
        $now = now();
        
        // Mapping for English to Indonesian day names
        $dayMap = [
            'Monday'    => 'Senin',
            'Tuesday'   => 'Selasa',
            'Wednesday' => 'Rabu',
            'Thursday'  => 'Kamis', 
            'Friday'    => 'Jumat',
            'Saturday'  => 'Sabtu',
            'Sunday'    => 'Minggu'
        ];
        
        $currentDayEnglish = $now->format('l'); // Get English day name (e.g. "Saturday")
        $currentDay = $dayMap[$currentDayEnglish] ?? $currentDayEnglish; // Convert to Indonesian
        
        $currentTime = $now->format('H:i:s');
    
        // Correct day order (Monday-Sunday)
        $dayOrder = [
            'Senin' => 1,
            'Selasa' => 2,
            'Rabu' => 3,
            'Kamis' => 4,
            'Jumat' => 5,
            'Sabtu' => 6,
            'Minggu' => 7
        ];
    
        // 1. First try to find today's upcoming ACTIVE schedules
        $todaysSchedule = JadwalBel::where('is_active', true)
            ->where('hari', $currentDay)
            ->where('waktu', '>', $currentTime)
            ->orderBy('waktu')
            ->first();
    
        if ($todaysSchedule) {
            return $this->formatScheduleResponse($todaysSchedule);
        }
    
        // 2. If no more today, find the next ACTIVE schedule in the week
        $allSchedules = JadwalBel::where('is_active', true)
            ->orderByRaw("FIELD(hari, 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Minggu')")
            ->orderBy('waktu')
            ->get();
    
        $currentDayValue = $dayOrder[$currentDay] ?? 0;
        $closestSchedule = null;
        $minDayDiff = 8; // More than 7 days
    
        foreach ($allSchedules as $schedule) {
            $scheduleDayValue = $dayOrder[$schedule->hari] ?? 0;
            
            // Calculate days difference
            $dayDiff = ($scheduleDayValue - $currentDayValue + 7) % 7;
            
            // If same day but time passed, add 7 days
            if ($dayDiff === 0 && $schedule->waktu <= $currentTime) {
                $dayDiff = 7;
            }
    
            // Find schedule with smallest day difference
            if ($dayDiff < $minDayDiff) {
                $minDayDiff = $dayDiff;
                $closestSchedule = $schedule;
            }
        }
    
        if ($closestSchedule) {
            return $this->formatScheduleResponse($closestSchedule);
        }
    
        return response()->json([
            'success' => false,
            'message' => 'Tidak ada jadwal aktif yang akan datang'
        ]);
    }
    
    private function formatScheduleResponse($schedule)
    {
        return response()->json([
            'success' => true,
            'next_schedule' => [
                'hari' => $schedule->hari,
                'time' => $schedule->waktu,
                'file_number' => $schedule->file_number,
                'is_active' => $schedule->is_active
            ]
        ]);
    }

    protected function validateSchedule(Request $request)
    {
        return $request->validate([
            'hari' => 'required|in:' . implode(',', JadwalBel::DAYS),
            'waktu' => 'required|date_format:H:i',
            'file_number' => 'required|string|size:4',
            'is_active' => 'sometimes|boolean'
        ]);
    }

    protected function logActivity($action, JadwalBel $schedule)
    {
        Log::info("{$action} - ID: {$schedule->id}, Hari: {$schedule->hari}, Waktu: {$schedule->waktu}, File: {$schedule->file_number}");
    }

    protected function logMqttActivity($action, $message)
    {
        $this->mqttService->publish(
            $this->mqttConfig['topics']['system']['logs'],
            json_encode([
                'action' => $action,
                'message' => $message,
                'timestamp' => Carbon::now()->toDateTimeString()
            ]),
            0
        );
    }
    
    protected function handleBellRing(string $message)
    {
        try {
            $data = json_decode($message, true);
            
            // Validasi data yang diterima
            if (!isset($data['hari']) || !isset($data['waktu']) || !isset($data['file_number']) || !isset($data['trigger_type'])) {
                Log::error('Invalid bell ring data format');
                return;
            }

            // Simpan ke history
            BellHistory::create([
                'hari' => $data['hari'],
                'waktu' => $data['waktu'],
                'file_number' => $data['file_number'],
                'trigger_type' => $data['trigger_type'],
                'ring_time' => Carbon::now(),
                'volume' => $data['volume'] ?? 15, // Default volume 15
                'repeat' => $data['repeat'] ?? 1   // Default repeat 1x
            ]);

            Log::info('Bell ring recorded to history', ['data' => $data]);

        } catch (\Exception $e) {
            Log::error('Error handling bell ring: ' . $e->getMessage());
        }
    }

    public function history(Request $request)
    {
        try {
            $query = BellHistory::query()->latest('ring_time');
            
            if ($request->filled('date')) {
                $query->whereDate('ring_time', $request->date);
            }
            
            if ($request->filled('search')) {
                $query->where(function($q) use ($request) {
                    $q->where('hari', 'like', '%'.$request->search.'%')
                    ->orWhere('file_number', 'like', '%'.$request->search.'%')
                    ->orWhere('trigger_type', 'like', '%'.$request->search.'%');
                });
            }
            
            $histories = $query->paginate(15);
            
            return view('admin.bel.history', [
                'histories' => $histories
            ]);
            
        } catch (\Exception $e) {
            Log::error('Error fetching history: ' . $e->getMessage());
            return back()->with('error', 'Gagal memuat riwayat bel');
        }
    }
}