package dev.lions.unionflow.server.api.dto.notification;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

@DisplayName("Tests pour ActionNotificationDTO")
class ActionNotificationDTOTest {

  @Test
  @DisplayName("Test de base - classe peut être instanciée")
  void testClasseInstanciable() {
    ActionNotificationDTO dto = new ActionNotificationDTO();
    assertThat(dto).isNotNull();
  }

  @Nested
  @DisplayName("Tests de construction")
  class ConstructionTests {

    @Test
    @DisplayName("Constructeur par défaut")
    void testConstructeurParDefaut() {
      ActionNotificationDTO dto = new ActionNotificationDTO();

      assertThat(dto).isNotNull();
      assertThat(dto.getFermeNotification()).isTrue();
      assertThat(dto.getNecessiteConfirmation()).isFalse();
      assertThat(dto.getEstDestructive()).isFalse();
      assertThat(dto.getOrdre()).isEqualTo(0);
      assertThat(dto.getEstActivee()).isTrue();
      assertThat(dto.getMaxExecutions()).isEqualTo(1);
      assertThat(dto.getNombreExecutions()).isEqualTo(0);
      assertThat(dto.getPeutEtreRepetee()).isFalse();
      assertThat(dto.getStyleBouton()).isEqualTo("primary");
      assertThat(dto.getTailleBouton()).isEqualTo("medium");
      assertThat(dto.getPositionBouton()).isEqualTo("right");
    }

    @Test
    @DisplayName("Constructeur avec paramètres essentiels")
    void testConstructeurAvecParametresEssentiels() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-123", "Confirmer", "confirm");

      assertThat(dto.getId()).isEqualTo("id-123");
      assertThat(dto.getLibelle()).isEqualTo("Confirmer");
      assertThat(dto.getTypeAction()).isEqualTo("confirm");
    }

    @Test
    @DisplayName("Constructeur pour action URL")
    void testConstructeurActionURL() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-456", "Ouvrir", "https://example.com", "open_in_new");

      assertThat(dto.getId()).isEqualTo("id-456");
      assertThat(dto.getLibelle()).isEqualTo("Ouvrir");
      assertThat(dto.getTypeAction()).isEqualTo("url");
      assertThat(dto.getUrl()).isEqualTo("https://example.com");
      assertThat(dto.getIcone()).isEqualTo("open_in_new");
    }

    @Test
    @DisplayName("Constructeur pour action de route")
    void testConstructeurActionRoute() {
      Map<String, String> parametres = new HashMap<>();
      parametres.put("page", "dashboard");
      ActionNotificationDTO dto = new ActionNotificationDTO("id-789", "Aller au dashboard", "/dashboard", "dashboard", parametres);

      assertThat(dto.getId()).isEqualTo("id-789");
      assertThat(dto.getLibelle()).isEqualTo("Aller au dashboard");
      assertThat(dto.getTypeAction()).isEqualTo("route");
      assertThat(dto.getRoute()).isEqualTo("/dashboard");
      assertThat(dto.getIcone()).isEqualTo("dashboard");
      assertThat(dto.getParametres()).isEqualTo(parametres);
    }
  }

  @Nested
  @DisplayName("Tests méthodes utilitaires")
  class MethodesUtilitairesTests {

    @Test
    @DisplayName("peutEtreExecutee - action activée et exécutions disponibles (nombreExecutions < maxExecutions)")
    void testPeutEtreExecuteeTrue() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setEstActivee(true);
      dto.setMaxExecutions(5);
      dto.setNombreExecutions(2);
      dto.setPeutEtreRepetee(false);

      assertThat(dto.peutEtreExecutee()).isTrue();
    }

    @Test
    @DisplayName("peutEtreExecutee - action désactivée")
    void testPeutEtreExecuteeDesactivee() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setEstActivee(false);
      dto.setMaxExecutions(5);
      dto.setNombreExecutions(2);

      assertThat(dto.peutEtreExecutee()).isFalse();
    }

    @Test
    @DisplayName("peutEtreExecutee - nombre max atteint sans répétition (nombreExecutions >= maxExecutions && !peutEtreRepetee)")
    void testPeutEtreExecuteeMaxAtteint() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setEstActivee(true);
      dto.setMaxExecutions(5);
      dto.setNombreExecutions(5);
      dto.setPeutEtreRepetee(false);

      assertThat(dto.peutEtreExecutee()).isFalse();
    }

    @Test
    @DisplayName("peutEtreExecutee - peut être répétée (peutEtreRepetee == true)")
    void testPeutEtreExecuteeRepetee() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setEstActivee(true);
      dto.setMaxExecutions(5);
      dto.setNombreExecutions(10);
      dto.setPeutEtreRepetee(true);

      assertThat(dto.peutEtreExecutee()).isTrue();
    }

    @Test
    @DisplayName("peutEtreExecutee - nombreExecutions == maxExecutions mais peutEtreRepetee == true")
    void testPeutEtreExecuteeMaxAtteintMaisRepetee() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setEstActivee(true);
      dto.setMaxExecutions(5);
      dto.setNombreExecutions(5);
      dto.setPeutEtreRepetee(true);

      assertThat(dto.peutEtreExecutee()).isTrue();
    }

    @Test
    @DisplayName("isExpiree - toujours false (non implémenté)")
    void testIsExpiree() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      assertThat(dto.isExpiree()).isFalse();
    }

    @Test
    @DisplayName("incrementerExecutions - null devient 1")
    void testIncrementerExecutionsNull() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setNombreExecutions(null);
      dto.incrementerExecutions();
      assertThat(dto.getNombreExecutions()).isEqualTo(1);
    }

    @Test
    @DisplayName("incrementerExecutions - incrémente")
    void testIncrementerExecutions() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setNombreExecutions(5);
      dto.incrementerExecutions();
      assertThat(dto.getNombreExecutions()).isEqualTo(6);
    }

    @Test
    @DisplayName("utilisateurAutorise - aucun rôle/permission requis")
    void testUtilisateurAutoriseAucunRequis() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(null);
      dto.setPermissionsRequises(null);

      assertThat(dto.utilisateurAutorise(new String[]{"USER"}, new String[]{"READ"})).isTrue();
    }

    @Test
    @DisplayName("utilisateurAutorise - rôle autorisé")
    void testUtilisateurAutoriseRoleAutorise() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(new String[]{"ADMIN", "USER"});
      dto.setPermissionsRequises(null);

      assertThat(dto.utilisateurAutorise(new String[]{"USER"}, new String[]{})).isTrue();
    }

    @Test
    @DisplayName("utilisateurAutorise - rôle non autorisé")
    void testUtilisateurAutoriseRoleNonAutorise() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(new String[]{"ADMIN"});
      dto.setPermissionsRequises(null);

      assertThat(dto.utilisateurAutorise(new String[]{"USER"}, new String[]{})).isFalse();
    }

    @Test
    @DisplayName("utilisateurAutorise - permission autorisée")
    void testUtilisateurAutorisePermissionAutorisee() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(null);
      dto.setPermissionsRequises(new String[]{"WRITE", "READ"});

      assertThat(dto.utilisateurAutorise(new String[]{}, new String[]{"READ"})).isTrue();
    }

    @Test
    @DisplayName("utilisateurAutorise - permission non autorisée")
    void testUtilisateurAutorisePermissionNonAutorisee() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(null);
      dto.setPermissionsRequises(new String[]{"WRITE"});

      assertThat(dto.utilisateurAutorise(new String[]{}, new String[]{"READ"})).isFalse();
    }

    @Test
    @DisplayName("utilisateurAutorise - rôle et permission requis")
    void testUtilisateurAutoriseRoleEtPermission() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(new String[]{"ADMIN"});
      dto.setPermissionsRequises(new String[]{"WRITE"});

      assertThat(dto.utilisateurAutorise(new String[]{"ADMIN"}, new String[]{"WRITE"})).isTrue();
      assertThat(dto.utilisateurAutorise(new String[]{"ADMIN"}, new String[]{"READ"})).isFalse();
      assertThat(dto.utilisateurAutorise(new String[]{"USER"}, new String[]{"WRITE"})).isFalse();
    }

    @Test
    @DisplayName("utilisateurAutorise - rôle autorisé dans plusieurs rôles")
    void testUtilisateurAutoriseRoleDansPlusieursRoles() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(new String[]{"ADMIN", "USER"});
      dto.setPermissionsRequises(null);

      assertThat(dto.utilisateurAutorise(new String[]{"USER", "GUEST"}, new String[]{})).isTrue();
    }

    @Test
    @DisplayName("utilisateurAutorise - permission autorisée dans plusieurs permissions")
    void testUtilisateurAutorisePermissionDansPlusieursPermissions() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(null);
      dto.setPermissionsRequises(new String[]{"WRITE", "READ"});

      assertThat(dto.utilisateurAutorise(new String[]{}, new String[]{"READ", "DELETE"})).isTrue();
    }

    @Test
    @DisplayName("utilisateurAutorise - rôles vides")
    void testUtilisateurAutoriseRolesVides() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(new String[0]);
      dto.setPermissionsRequises(null);

      assertThat(dto.utilisateurAutorise(new String[]{"USER"}, new String[]{})).isTrue();
    }

    @Test
    @DisplayName("utilisateurAutorise - permissions vides")
    void testUtilisateurAutorisePermissionsVides() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setRolesAutorises(null);
      dto.setPermissionsRequises(new String[0]);

      assertThat(dto.utilisateurAutorise(new String[]{}, new String[]{"READ"})).isTrue();
    }

    @ParameterizedTest
    @CsvSource({
        "confirm, #4CAF50",
        "cancel, #F44336",
        "info, #2196F3",
        "warning, #FF9800",
        "url, #2196F3",
        "route, #2196F3",
        "unknown, #9E9E9E"
    })
    @DisplayName("getCouleurParDefaut - toutes les couleurs")
    void testGetCouleurParDefaut(String typeAction, String expected) {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", typeAction);
      dto.setCouleur(null);

      assertThat(dto.getCouleurParDefaut()).isEqualTo(expected);
    }

    @Test
    @DisplayName("getCouleurParDefaut - couleur personnalisée")
    void testGetCouleurParDefautPersonnalisee() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setCouleur("#FF0000");

      assertThat(dto.getCouleurParDefaut()).isEqualTo("#FF0000");
    }

    @ParameterizedTest
    @CsvSource({
        "confirm, check",
        "cancel, close",
        "info, info",
        "warning, warning",
        "url, open_in_new",
        "route, arrow_forward",
        "call, phone",
        "message, message",
        "email, email",
        "share, share",
        "unknown, touch_app"
    })
    @DisplayName("getIconeParDefaut - toutes les icônes")
    void testGetIconeParDefaut(String typeAction, String expected) {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", typeAction);
      dto.setIcone(null);

      assertThat(dto.getIconeParDefaut()).isEqualTo(expected);
    }

    @Test
    @DisplayName("getIconeParDefaut - icône personnalisée")
    void testGetIconeParDefautPersonnalisee() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-1", "Test", "confirm");
      dto.setIcone("custom_icon");

      assertThat(dto.getIconeParDefaut()).isEqualTo("custom_icon");
    }

    @Test
    @DisplayName("creerActionConfirmation - factory method")
    void testCreerActionConfirmation() {
      ActionNotificationDTO action = ActionNotificationDTO.creerActionConfirmation("id-1", "Confirmer");

      assertThat(action.getId()).isEqualTo("id-1");
      assertThat(action.getLibelle()).isEqualTo("Confirmer");
      assertThat(action.getTypeAction()).isEqualTo("confirm");
      assertThat(action.getCouleur()).isEqualTo("#4CAF50");
      assertThat(action.getIcone()).isEqualTo("check");
      assertThat(action.getStyleBouton()).isEqualTo("primary");
    }

    @Test
    @DisplayName("creerActionAnnulation - factory method")
    void testCreerActionAnnulation() {
      ActionNotificationDTO action = ActionNotificationDTO.creerActionAnnulation("id-2", "Annuler");

      assertThat(action.getId()).isEqualTo("id-2");
      assertThat(action.getLibelle()).isEqualTo("Annuler");
      assertThat(action.getTypeAction()).isEqualTo("cancel");
      assertThat(action.getCouleur()).isEqualTo("#F44336");
      assertThat(action.getIcone()).isEqualTo("close");
      assertThat(action.getStyleBouton()).isEqualTo("outline");
      assertThat(action.getEstDestructive()).isTrue();
    }

    @Test
    @DisplayName("creerActionNavigation - factory method")
    void testCreerActionNavigation() {
      ActionNotificationDTO action = ActionNotificationDTO.creerActionNavigation("id-3", "Voir", "/dashboard");

      assertThat(action.getId()).isEqualTo("id-3");
      assertThat(action.getLibelle()).isEqualTo("Voir");
      assertThat(action.getTypeAction()).isEqualTo("route");
      assertThat(action.getRoute()).isEqualTo("/dashboard");
      assertThat(action.getCouleur()).isEqualTo("#2196F3");
      assertThat(action.getIcone()).isEqualTo("arrow_forward");
    }
  }

  @Nested
  @DisplayName("Tests getters/setters")
  class GettersSettersTests {

    @Test
    @DisplayName("Test tous les getters/setters")
    void testTousLesGettersSetters() {
      ActionNotificationDTO dto = new ActionNotificationDTO();
      Map<String, String> parametres = new HashMap<>();
      parametres.put("key", "value");
      Map<String, Object> donnees = new HashMap<>();
      donnees.put("meta", "data");

      dto.setId("id-123");
      dto.setLibelle("Libellé");
      dto.setDescription("Description");
      dto.setTypeAction("type");
      dto.setIcone("icon");
      dto.setCouleur("#FF0000");
      dto.setUrl("https://example.com");
      dto.setRoute("/route");
      dto.setParametres(parametres);
      dto.setFermeNotification(false);
      dto.setNecessiteConfirmation(true);
      dto.setMessageConfirmation("Confirmer ?");
      dto.setEstDestructive(true);
      dto.setOrdre(1);
      dto.setEstActivee(false);
      dto.setConditionAffichage("condition");
      dto.setRolesAutorises(new String[]{"ADMIN"});
      dto.setPermissionsRequises(new String[]{"WRITE"});
      dto.setDelaiExpirationMinutes(60);
      dto.setMaxExecutions(10);
      dto.setNombreExecutions(5);
      dto.setPeutEtreRepetee(true);
      dto.setStyleBouton("secondary");
      dto.setTailleBouton("large");
      dto.setPositionBouton("left");
      dto.setDonneesPersonnalisees(donnees);

      assertThat(dto.getId()).isEqualTo("id-123");
      assertThat(dto.getLibelle()).isEqualTo("Libellé");
      assertThat(dto.getDescription()).isEqualTo("Description");
      assertThat(dto.getTypeAction()).isEqualTo("type");
      assertThat(dto.getIcone()).isEqualTo("icon");
      assertThat(dto.getCouleur()).isEqualTo("#FF0000");
      assertThat(dto.getUrl()).isEqualTo("https://example.com");
      assertThat(dto.getRoute()).isEqualTo("/route");
      assertThat(dto.getParametres()).isEqualTo(parametres);
      assertThat(dto.getFermeNotification()).isFalse();
      assertThat(dto.getNecessiteConfirmation()).isTrue();
      assertThat(dto.getMessageConfirmation()).isEqualTo("Confirmer ?");
      assertThat(dto.getEstDestructive()).isTrue();
      assertThat(dto.getOrdre()).isEqualTo(1);
      assertThat(dto.getEstActivee()).isFalse();
      assertThat(dto.getConditionAffichage()).isEqualTo("condition");
      assertThat(dto.getRolesAutorises()).containsExactly("ADMIN");
      assertThat(dto.getPermissionsRequises()).containsExactly("WRITE");
      assertThat(dto.getDelaiExpirationMinutes()).isEqualTo(60);
      assertThat(dto.getMaxExecutions()).isEqualTo(10);
      assertThat(dto.getNombreExecutions()).isEqualTo(5);
      assertThat(dto.getPeutEtreRepetee()).isTrue();
      assertThat(dto.getStyleBouton()).isEqualTo("secondary");
      assertThat(dto.getTailleBouton()).isEqualTo("large");
      assertThat(dto.getPositionBouton()).isEqualTo("left");
      assertThat(dto.getDonneesPersonnalisees()).isEqualTo(donnees);
    }
  }

  @Nested
  @DisplayName("Tests toString")
  class ToStringTests {

    @Test
    @DisplayName("toString - contient les informations essentielles")
    void testToString() {
      ActionNotificationDTO dto = new ActionNotificationDTO("id-123", "Confirmer", "confirm");

      String toString = dto.toString();
      assertThat(toString).contains("ActionNotificationDTO");
      assertThat(toString).contains("id-123");
      assertThat(toString).contains("Confirmer");
      assertThat(toString).contains("confirm");
    }
  }
}

