package dev.lions.unionflow.client.view;

import dev.lions.unionflow.server.api.dto.organisation.response.OrganisationResponse;
import dev.lions.unionflow.server.api.dto.organisation.response.OrganisationSummaryResponse;
import dev.lions.unionflow.server.api.dto.common.PagedResponse;
import dev.lions.unionflow.client.service.OrganisationService;
import dev.lions.unionflow.client.service.ErrorHandlerService;
import dev.lions.unionflow.client.service.RestClientExceptionMapper;
import dev.lions.unionflow.client.service.RetryService;
import dev.lions.unionflow.client.service.TypeCatalogueService;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.model.SelectItem;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Bean de consultation et d'édition d'une organisation (fiche détaillée).
 * Supporte le basculement entre mode lecture et mode édition inline.
 */
@Named("organisationDetailBean")
@ViewScoped
public class OrganisationDetailBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(OrganisationDetailBean.class);

    @Inject
    @RestClient
    OrganisationService organisationService;

    @Inject
    ErrorHandlerService errorHandler;

    @Inject
    RetryService retryService;

    @Inject
    TypeCatalogueService typeCatalogueService;

    private OrganisationResponse organisation;
    private UUID organisationId;
    private boolean modeEdition = false;

    @PostConstruct
    public void init() {
        java.util.Map<String, String> params = FacesContext.getCurrentInstance()
                .getExternalContext()
                .getRequestParameterMap();

        String idParam = params.get("id");

        if (idParam != null && !idParam.isBlank()) {
            try {
                organisationId = UUID.fromString(idParam);
                chargerOrganisation();
                if ("edit".equalsIgnoreCase(params.get("mode"))) {
                    modeEdition = true;
                }
            } catch (IllegalArgumentException e) {
                LOG.errorf(e, "ID d'organisation invalide: %s", idParam);
                errorHandler.showWarning("Organisation introuvable", "Identifiant invalide.");
            }
        } else {
            errorHandler.showWarning("Organisation introuvable", "Aucun identifiant fourni.");
        }
    }

    public void chargerOrganisation() {
        if (organisationId == null) return;
        try {
            organisation = retryService.executeWithRetrySupplier(
                    () -> organisationService.obtenirParId(organisationId),
                    "chargement des détails d'une organisation"
            );
            if (organisation != null) {
                String libelle = typeCatalogueService.resolveLibelle(organisation.getTypeOrganisation());
                organisation.setTypeOrganisationLibelle(libelle);
            }
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du chargement de l'organisation");
            errorHandler.handleException(e, "lors du chargement des détails d'une organisation", null);
        }
    }

    // ── Mode édition ────────────────────────────────────────────────────────

    public void activerEdition() {
        modeEdition = true;
    }

    public void annulerEdition() {
        chargerOrganisation();
        modeEdition = false;
    }

    // ── Mapping champ backend → client ID JSF (formDetail:<componentId>) ────
    private static final Map<String, String> VIOLATION_FIELD_MAP = Map.of(
            "codePostal",           "formDetail:codePostal",
            "telephoneSecondaire",  "formDetail:telephone2",
            "telephone",            "formDetail:telephone",
            "siteWeb",              "formDetail:siteWeb",
            "logo",                 "formDetail:logo",
            "email",                "formDetail:email",
            "emailSecondaire",      "formDetail:email2",
            "nom",                  "formDetail:nom",
            "quartier",             "formDetail:quartier"
    );

    public void sauvegarder() {
        if (organisationId == null || organisation == null) return;
        try {
            sanitiserAvantEnvoi(organisation);
            OrganisationResponse maj = retryService.executeWithRetrySupplier(
                    () -> organisationService.modifier(organisationId, organisation),
                    "modification de l'organisation"
            );
            organisation = maj;
            if (organisation != null) {
                String libelle = typeCatalogueService.resolveLibelle(organisation.getTypeOrganisation());
                organisation.setTypeOrganisationLibelle(libelle);
            }
            modeEdition = false;
            errorHandler.showSuccess("Succès", "Organisation modifiée avec succès.");
        } catch (RestClientExceptionMapper.BadRequestException e) {
            gererViolationsContraintes(e.getMessage());
        } catch (Exception e) {
            errorHandler.handleException(e, "lors de la sauvegarde de l'organisation", null);
        }
    }

    /**
     * Normalise les champs optionnels avant envoi au backend pour éviter les
     * rejets @Pattern sur des données déjà en base avec un format légèrement incorrect.
     * Règles : vide → null, URL sans schème → ajoute https://, code postal → majuscules.
     */
    private void sanitiserAvantEnvoi(OrganisationResponse org) {
        org.setNom(trimOrKeep(org.getNom()));
        org.setNomCourt(emptyToNull(org.getNomCourt()));
        org.setQuartier(emptyToNull(org.getQuartier()));
        org.setVille(emptyToNull(org.getVille()));
        org.setRegion(emptyToNull(org.getRegion()));
        org.setPays(emptyToNull(org.getPays()));
        org.setEmail(emptyToNull(org.getEmail()));
        org.setEmailSecondaire(emptyToNull(org.getEmailSecondaire()));
        org.setTelephone(normaliserTelephone(org.getTelephone()));
        org.setTelephoneSecondaire(normaliserTelephone(org.getTelephoneSecondaire()));
        org.setSiteWeb(normaliserUrl(org.getSiteWeb()));
        org.setCodePostal(normaliserCodePostal(org.getCodePostal()));
        org.setLogo(emptyToNull(org.getLogo()));
        org.setDevise(emptyToNull(org.getDevise()));
    }

    private String emptyToNull(String s) {
        return (s == null || s.trim().isEmpty()) ? null : s.trim();
    }

    private String trimOrKeep(String s) {
        return s != null ? s.trim() : null;
    }

    private String normaliserTelephone(String phone) {
        if (phone == null || phone.trim().isEmpty()) return null;
        String clean = phone.trim();
        if (clean.matches("^\\+?[0-9\\s\\-\\(\\)]{8,20}$")) return clean;
        // Supprimer les caractères invalides (points, slashs, etc.)
        String cleaned = clean.replaceAll("[^0-9+\\s\\-\\(\\)]", "");
        if (cleaned.matches("^\\+?[0-9\\s\\-\\(\\)]{8,20}$")) return cleaned;
        return null;
    }

    private String normaliserUrl(String url) {
        if (url == null || url.trim().isEmpty()) return null;
        String clean = url.trim();
        if (clean.matches("^https?://.*")) return clean;
        String withScheme = "https://" + clean;
        if (withScheme.matches("^https?://[\\w\\-]+(\\.[\\w\\-]+).*")) return withScheme;
        return null;
    }

    private String normaliserCodePostal(String cp) {
        if (cp == null || cp.trim().isEmpty()) return null;
        String clean = cp.trim().toUpperCase();
        if (clean.matches("^[0-9A-Z\\-\\s]{3,10}$")) return clean;
        // Supprimer les caractères non autorisés
        String cleaned = clean.replaceAll("[^0-9A-Z\\-\\s]", "").trim();
        if (cleaned.length() > 10) cleaned = cleaned.substring(0, 10).trim();
        if (cleaned.length() >= 3 && cleaned.matches("^[0-9A-Z\\-\\s]{3,10}$")) return cleaned;
        return null;
    }

    /**
     * Parse la réponse de violation de contrainte Quarkus et ajoute un
     * {@link FacesMessage} sur chaque composant JSF concerné pour le highlighting.
     */
    private void gererViolationsContraintes(String errorMessage) {
        String json = errorMessage;
        int jsonStart = errorMessage.indexOf('{');
        if (jsonStart >= 0) json = errorMessage.substring(jsonStart);

        Pattern p = Pattern.compile(
                "\"field\"\\s*:\\s*\"([^\"]+)\"\\s*,\\s*\"message\"\\s*:\\s*\"([^\"]+)\"");
        Matcher m = p.matcher(json);

        boolean hasFieldMessages = false;
        FacesContext fc = FacesContext.getCurrentInstance();

        while (m.find()) {
            String fieldPath = m.group(1);
            String message   = m.group(2);
            String fieldName = fieldPath.substring(fieldPath.lastIndexOf('.') + 1);

            String componentId = VIOLATION_FIELD_MAP.get(fieldName);
            if (componentId != null) {
                fc.addMessage(componentId, new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null));
                hasFieldMessages = true;
            } else {
                fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        "Champ invalide : " + fieldName, message));
                hasFieldMessages = true;
            }
        }

        if (!hasFieldMessages) {
            errorHandler.handleException(
                    new RestClientExceptionMapper.BadRequestException(errorMessage),
                    "lors de la sauvegarde de l'organisation", null);
        }
    }

    public void actualiser() {
        if (organisationId != null) {
            chargerOrganisation();
            errorHandler.showSuccess("Actualisation", "Les données ont été actualisées.");
        }
    }

    // ── Données pour le formulaire (DRY : même interface que OrganisationsBean) ──

    public List<SelectItem> getTypesSelectItemsForForm() {
        List<SelectItem> items = typeCatalogueService.getSelectItems("Sélectionner un type...");
        // Si la valeur actuelle n'est pas dans le catalogue, l'ajouter comme option
        // pour que le binding JSF ne la considère pas comme absente
        if (organisation != null && organisation.getTypeOrganisation() != null
                && !organisation.getTypeOrganisation().isBlank()) {
            boolean present = items.stream()
                    .anyMatch(i -> organisation.getTypeOrganisation().equals(i.getValue()));
            if (!present) {
                items.add(new SelectItem(
                        organisation.getTypeOrganisation(),
                        typeCatalogueService.resolveLibelle(organisation.getTypeOrganisation())
                ));
            }
        }
        return items;
    }

    public List<String> completerVilles(String query) {
        List<String> villes = List.of(
                "Abidjan", "Bouaké", "Daloa", "Korhogo", "San-Pédro",
                "Yamoussoukro", "Man", "Divo", "Gagnoa", "Abengourou",
                "Grand-Bassam", "Bingerville", "Anyama", "Agboville",
                "Dabou", "Adzopé", "Bouaflé", "Issia", "Sinfra", "Vavoua");
        if (query == null || query.trim().isEmpty()) return villes;
        String q = query.toLowerCase();
        return villes.stream().filter(v -> v.toLowerCase().contains(q)).collect(Collectors.toList());
    }

    public List<String> completerRegions(String query) {
        List<String> regions = List.of(
                "Lagunes", "Haut-Sassandra", "Savanes", "Vallée du Bandama",
                "Moyen-Comoé", "Worodougou", "Sud-Comoé", "Marahoué",
                "Sud-Bandama", "Gbêkê", "Nawa", "Gbôklé", "Cavally",
                "Guémon", "Tonkpi", "Bagoué", "Poro", "Tchologo", "Béré");
        if (query == null || query.trim().isEmpty()) return regions;
        String q = query.toLowerCase();
        return regions.stream().filter(r -> r.toLowerCase().contains(q)).collect(Collectors.toList());
    }

    public List<OrganisationSummaryResponse> rechercherOrganisations(String query) {
        if (query == null || query.trim().isEmpty()) return List.of();
        try {
            PagedResponse<OrganisationSummaryResponse> response =
                    organisationService.rechercher(query, null, null, null, null, 0, 100);
            return (response != null && response.getData() != null) ? response.getData() : List.of();
        } catch (Exception e) {
            LOG.errorf(e, "Erreur recherche organisations pour '%s'", query);
            return List.of();
        }
    }

    // ── Propriétés calculées ─────────────────────────────────────────────────

    public String getInitiales() {
        if (organisation == null || organisation.getNom() == null) return "?";
        String[] mots = organisation.getNom().trim().split("\\s+");
        if (mots.length >= 2) {
            return String.valueOf(mots[0].charAt(0)).toUpperCase()
                    + String.valueOf(mots[1].charAt(0)).toUpperCase();
        }
        return String.valueOf(mots[0].charAt(0)).toUpperCase();
    }

    public String getTypeOrganisationLibelle() {
        if (organisation == null) return "";
        return organisation.getTypeOrganisationLibelle() != null ? organisation.getTypeOrganisationLibelle() : "";
    }

    public String getStatutLibelle() {
        if (organisation == null) return "Non renseigné";
        if (organisation.getStatutLibelle() != null && !organisation.getStatutLibelle().isBlank())
            return organisation.getStatutLibelle();
        if (organisation.getStatut() == null) return "Non renseigné";
        return switch (organisation.getStatut()) {
            case "ACTIVE" -> "Active";
            case "INACTIVE" -> "Inactive";
            case "SUSPENDUE" -> "Suspendue";
            case "DISSOLUE" -> "Dissoute";
            default -> organisation.getStatut();
        };
    }

    public String getStatutSeverity() {
        if (organisation == null) return "secondary";
        if (organisation.getStatutSeverity() != null && !organisation.getStatutSeverity().isBlank())
            return organisation.getStatutSeverity();
        if (organisation.getStatut() == null) return "secondary";
        return switch (organisation.getStatut()) {
            case "ACTIVE" -> "success";
            case "INACTIVE" -> "warning";
            case "SUSPENDUE", "DISSOLUE" -> "danger";
            default -> "secondary";
        };
    }

    public String getAdresseComplete() {
        if (organisation == null) return "";
        StringBuilder sb = new StringBuilder();
        appendSegment(sb, organisation.getAdresse());
        appendSegment(sb, organisation.getQuartier());
        appendSegment(sb, organisation.getVille());
        appendSegment(sb, organisation.getRegion());
        appendSegment(sb, organisation.getPays());
        return sb.toString();
    }

    private void appendSegment(StringBuilder sb, String val) {
        if (val != null && !val.trim().isEmpty()) {
            if (sb.length() > 0) sb.append(", ");
            sb.append(val.trim());
        }
    }

    // ── Getters ──────────────────────────────────────────────────────────────

    public OrganisationResponse getOrganisation() { return organisation; }

    public boolean isModeEdition() { return modeEdition; }
}
