package com.walkguide.service;

import com.walkguide.dto.request.HardwareShortcutUpdateRequest;
import com.walkguide.dto.response.HardwareShortcutResponse;
import com.walkguide.entity.HardwareShortcut;
import com.walkguide.enums.HardwareShortcutKey;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.HardwareShortcutRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@DisplayName("HardwareShortcutService Unit Tests")
class HardwareShortcutServiceTest {

    @Mock
    HardwareShortcutRepository hardwareShortcutRepository;

    @InjectMocks
    HardwareShortcutService hardwareShortcutService;

    private HardwareShortcut shortcutCallGuardian;
    private HardwareShortcut shortcutSendSos;

    @BeforeEach
    void setUp() {
        shortcutCallGuardian = HardwareShortcut.builder()
                .id(1L)
                .userId(10L)
                .shortcutKey(HardwareShortcutKey.CALL_GUARDIAN)
                .buttonName("Volume Up")
                .buttonCode(24)
                .enabled(true)
                .build();

        shortcutSendSos = HardwareShortcut.builder()
                .id(2L)
                .userId(10L)
                .shortcutKey(HardwareShortcutKey.SEND_SOS)
                .buttonName("Volume Down")
                .buttonCode(25)
                .enabled(false)
                .build();
    }

    // ===== getAll TESTS =====

    @Test
    @DisplayName("getAll - harus return semua shortcut milik user sebagai list response")
    void getAll_shouldReturnAllShortcutsForUser() {
        when(hardwareShortcutRepository.findByUserId(10L))
                .thenReturn(List.of(shortcutCallGuardian, shortcutSendSos));

        List<HardwareShortcutResponse> result = hardwareShortcutService.getAll(10L);

        assertThat(result).hasSize(2);
        assertThat(result.get(0).getShortcutKey()).isEqualTo("CALL_GUARDIAN");
        assertThat(result.get(0).getButtonName()).isEqualTo("Volume Up");
        assertThat(result.get(0).getButtonCode()).isEqualTo(24);
        assertThat(result.get(0).getEnabled()).isTrue();

        assertThat(result.get(1).getShortcutKey()).isEqualTo("SEND_SOS");
        assertThat(result.get(1).getEnabled()).isFalse();
    }

    @Test
    @DisplayName("getAll - harus return list kosong jika user tidak punya shortcut")
    void getAll_emptyList_whenUserHasNoShortcuts() {
        when(hardwareShortcutRepository.findByUserId(99L)).thenReturn(List.of());

        List<HardwareShortcutResponse> result = hardwareShortcutService.getAll(99L);

        assertThat(result).isEmpty();
    }

    // ===== update TESTS =====

    @Test
    @DisplayName("update - harus update buttonName, buttonCode, dan enabled jika semua field diisi")
    void update_allFields_shouldUpdateAllFields() {
        HardwareShortcutUpdateRequest req = new HardwareShortcutUpdateRequest();
        req.setShortcutKey("CALL_GUARDIAN");
        req.setButtonName("Power Button");
        req.setButtonCode(26);
        req.setEnabled(false);

        when(hardwareShortcutRepository.findByUserId(10L))
                .thenReturn(List.of(shortcutCallGuardian, shortcutSendSos));
        when(hardwareShortcutRepository.save(any(HardwareShortcut.class)))
                .thenAnswer(inv -> inv.getArgument(0));

        HardwareShortcutResponse result = hardwareShortcutService.update(10L, req);

        assertThat(result.getButtonName()).isEqualTo("Power Button");
        assertThat(result.getButtonCode()).isEqualTo(26);
        assertThat(result.getEnabled()).isFalse();
        assertThat(result.getShortcutKey()).isEqualTo("CALL_GUARDIAN");
    }

    @Test
    @DisplayName("update - field null tidak boleh mengubah nilai yang sudah ada")
    void update_nullFields_shouldNotOverrideExistingValues() {
        HardwareShortcutUpdateRequest req = new HardwareShortcutUpdateRequest();
        req.setShortcutKey("CALL_GUARDIAN");
        req.setButtonName(null);     // tidak diubah
        req.setButtonCode(null);     // tidak diubah
        req.setEnabled(null);        // tidak diubah

        when(hardwareShortcutRepository.findByUserId(10L))
                .thenReturn(List.of(shortcutCallGuardian));
        when(hardwareShortcutRepository.save(any(HardwareShortcut.class)))
                .thenAnswer(inv -> inv.getArgument(0));

        HardwareShortcutResponse result = hardwareShortcutService.update(10L, req);

        // Nilai lama harus tetap
        assertThat(result.getButtonName()).isEqualTo("Volume Up");
        assertThat(result.getButtonCode()).isEqualTo(24);
        assertThat(result.getEnabled()).isTrue();
    }

    @Test
    @DisplayName("update - shortcutKey tidak ditemukan harus throw ResourceNotFoundException")
    void update_shortcutKeyNotFound_shouldThrow() {
        HardwareShortcutUpdateRequest req = new HardwareShortcutUpdateRequest();
        req.setShortcutKey("START_WALKGUIDE"); // tidak ada di list user

        when(hardwareShortcutRepository.findByUserId(10L))
                .thenReturn(List.of(shortcutCallGuardian)); // hanya CALL_GUARDIAN

        assertThatThrownBy(() -> hardwareShortcutService.update(10L, req))
                .isInstanceOf(ResourceNotFoundException.class)
                .hasMessageContaining("Shortcut tidak ditemukan");

        verify(hardwareShortcutRepository, never()).save(any());
    }

    @Test
    @DisplayName("update - harus memanggil repository.save dengan entity yang sudah dimodifikasi")
    void update_shouldPersistChangesToRepository() {
        HardwareShortcutUpdateRequest req = new HardwareShortcutUpdateRequest();
        req.setShortcutKey("SEND_SOS");
        req.setEnabled(true);
        req.setButtonName("Camera Button");

        when(hardwareShortcutRepository.findByUserId(10L))
                .thenReturn(List.of(shortcutCallGuardian, shortcutSendSos));
        when(hardwareShortcutRepository.save(any(HardwareShortcut.class)))
                .thenAnswer(inv -> inv.getArgument(0));

        hardwareShortcutService.update(10L, req);

        ArgumentCaptor<HardwareShortcut> captor = ArgumentCaptor.forClass(HardwareShortcut.class);
        verify(hardwareShortcutRepository).save(captor.capture());

        HardwareShortcut saved = captor.getValue();
        assertThat(saved.getShortcutKey()).isEqualTo(HardwareShortcutKey.SEND_SOS);
        assertThat(saved.getEnabled()).isTrue();
        assertThat(saved.getButtonName()).isEqualTo("Camera Button");
    }

    @Test
    @DisplayName("update - shortcutKey enum invalid harus throw IllegalArgumentException")
    void update_invalidEnumKey_shouldThrow() {
        HardwareShortcutUpdateRequest req = new HardwareShortcutUpdateRequest();
        req.setShortcutKey("INVALID_KEY_XYZ");

        assertThatThrownBy(() -> hardwareShortcutService.update(10L, req))
                .isInstanceOf(IllegalArgumentException.class);
    }
}