"""
Vues CMS pour le rendu des pages et les endpoints HTMX.
"""
import json
from django.http import JsonResponse, Http404
from django.views.decorators.http import require_POST, require_GET
from django.views.decorators.csrf import csrf_protect
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import get_object_or_404, render
from django.views.generic import TemplateView, DetailView

from django.contrib import messages
from django.shortcuts import redirect

from traduction.helpers import prefetch_translations

from .models import Page, PageSection, Menu, MenuItem
from .forms import PageSectionFieldValueForm


# =============================================================================
# Vues Admin pour le reordonnancement
# =============================================================================

@staff_member_required
@require_GET
def reorder_sections_view(request, page_id):
    """
    Vue admin pour reordonner les sections d'une page via drag-drop.
    """
    page = get_object_or_404(Page, pk=page_id)
    sections = PageSection.objects.filter(page=page).select_related('section').order_by('order')

    return render(request, 'admin/cms/reorder_sections.html', {
        'page': page,
        'sections': sections,
        'title': f'Reordonner les sections - {page.name}',
    })


@staff_member_required
@require_GET
def reorder_menu_items_view(request, menu_id):
    """
    Vue admin pour reordonner les elements d'un menu via drag-drop.
    Affiche tous les elements (parents et enfants) avec indentation.
    """
    menu = get_object_or_404(Menu, pk=menu_id)

    # Recuperer tous les elements du menu
    all_items = MenuItem.objects.filter(menu=menu).select_related('icon', 'page', 'parent').order_by('parent__order', 'order')

    # Organiser hierarchiquement pour l'affichage
    root_items = []
    children_map = {}

    for item in all_items:
        if item.parent_id is None:
            root_items.append(item)
        else:
            if item.parent_id not in children_map:
                children_map[item.parent_id] = []
            children_map[item.parent_id].append(item)

    # Trier les root items par ordre
    root_items.sort(key=lambda x: x.order)

    # Construire la liste plate avec niveau d'indentation
    flat_items = []
    for item in root_items:
        flat_items.append({'item': item, 'level': 0})
        # Ajouter les enfants
        if item.id in children_map:
            children = sorted(children_map[item.id], key=lambda x: x.order)
            for child in children:
                flat_items.append({'item': child, 'level': 1})
                # Ajouter les petits-enfants si existent
                if child.id in children_map:
                    grandchildren = sorted(children_map[child.id], key=lambda x: x.order)
                    for grandchild in grandchildren:
                        flat_items.append({'item': grandchild, 'level': 2})

    return render(request, 'admin/cms/reorder_menu_items.html', {
        'menu': menu,
        'items': flat_items,
        'root_items': root_items,
        'children_map': children_map,
        'title': f'Reordonner les elements - {menu.name}',
    })


@staff_member_required
def edit_pagesection_fields(request, pagesection_id):
    """
    Vue admin pour editer les champs personnalises d'un PageSection.
    """
    page_section = get_object_or_404(
        PageSection.objects.select_related('page', 'section'),
        pk=pagesection_id
    )

    # Verifier que la section a des champs personnalises
    if not page_section.section.custom_fields.exists():
        messages.warning(request, 'Cette section n\'a pas de champs personnalises definis.')
        return redirect('admin:cms_page_change', page_section.page_id)

    if request.method == 'POST':
        form = PageSectionFieldValueForm(
            request.POST,
            request.FILES,
            page_section=page_section
        )
        if form.is_valid():
            form.save()
            messages.success(request, 'Champs personnalises mis a jour avec succes.')
            return redirect('admin:cms_page_change', page_section.page_id)
    else:
        form = PageSectionFieldValueForm(page_section=page_section)

    return render(request, 'admin/cms/edit_pagesection_fields.html', {
        'form': form,
        'page_section': page_section,
        'page': page_section.page,
        'section': page_section.section,
        'title': f'Champs personnalises - {page_section.section.name}',
        'opts': PageSection._meta,
        'has_change_permission': True,
    })


# =============================================================================
# Vues Publiques
# =============================================================================


class HomeView(TemplateView):
    """
    Vue pour la page d'accueil.
    Utilise la page marquee comme is_homepage=True.
    """

    def get_template_names(self):
        page = self.get_page()
        if page:
            return [page.template.get_template_name()]
        return ['cms/home.html']

    def get_page(self):
        try:
            return Page.objects.select_related('template').get(
                is_homepage=True,
                is_active=True
            )
        except Page.DoesNotExist:
            return None

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        page = self.get_page()
        if page:
            context['page'] = page
            context['sections'] = page.get_ordered_sections()
        return context


class PageView(DetailView):
    """
    Vue pour afficher une page CMS par son slug.
    """
    model = Page
    context_object_name = 'page'

    def get_queryset(self):
        return Page.objects.filter(is_active=True).select_related('template')

    def get_template_names(self):
        return [self.object.template.get_template_name()]

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['sections'] = self.object.get_ordered_sections()
        return context


def home_view(request):
    """
    Vue fonctionnelle pour la page d'accueil.
    Prefetch les traductions pour la page et ses sections.
    """
    try:
        page = Page.objects.select_related('template').get(
            is_homepage=True,
            is_active=True
        )
    except Page.DoesNotExist:
        raise Http404("Aucune page d'accueil n'est configuree.")

    # Recuperer le code de la langue courante
    language_code = getattr(request, 'current_language_code', 'fr')

    # Prefetch les traductions pour la page
    prefetch_translations([page], language_code=language_code)

    # Recuperer les sections ordonnees
    sections = page.get_ordered_sections()

    # Prefetch les traductions pour les sections
    if sections:
        prefetch_translations(list(sections), language_code=language_code)

    context = {
        'page': page,
        'sections': sections,
    }

    # Ajouter map_settings si disponible
    try:
        from apps.core.models import UniversityMapSettings
        context['map_settings'] = UniversityMapSettings.get_settings()
    except (ImportError, Exception):
        pass

    return render(request, page.template.get_template_name(), context)


def page_view(request, slug):
    """
    Vue fonctionnelle pour afficher une page par son slug.
    Prefetch les traductions pour la page et ses sections.
    """
    page = get_object_or_404(
        Page.objects.select_related('template'),
        slug=slug,
        is_active=True
    )

    # Recuperer le code de la langue courante
    language_code = getattr(request, 'current_language_code', 'fr')

    # Prefetch les traductions pour la page
    prefetch_translations([page], language_code=language_code)

    # Recuperer les sections ordonnees
    sections = page.get_ordered_sections()

    # Prefetch les traductions pour les sections
    if sections:
        prefetch_translations(list(sections), language_code=language_code)

    # Ajouter les donnees contextuelles pour les sections
    context = {
        'page': page,
        'sections': sections,
    }

    # Ajouter map_settings si disponible
    try:
        from apps.core.models import UniversityMapSettings
        context['map_settings'] = UniversityMapSettings.get_settings()
    except (ImportError, Exception):
        pass

    return render(request, page.template.get_template_name(), context)



# def get_page(request, slug):
#     # 1. Tentative de récupération de la page
#     page = Page.objects.filter(slug=page_slug).first()

#     # 2. Gestion de l'absence de page (Sécurité boucle infinie)
#     if not page:
#         if page_slug == 'home':
#             # Si on cherchait déjà 'home' et qu'elle n'est pas là, c'est une vraie 404.
#             # Cela brise la boucle infinie.
#             raise Http404("La page d'accueil est introuvable.")

#         # Sinon, on redirige vers la home (soit récursivement, soit via redirect)
#         # Option A (Votre méthode actuelle : garde l'URL mais affiche la home)
#         return get_page(request, 'home')

#         # Option B (Meilleure pratique SEO : redirige le navigateur vers /home)
#         # return redirect('nom_de_votre_url_home')

#     # --- Le reste du code ---

#     # Optimisation : select_related pour éviter des requêtes SQL supplémentaires si 'section' est une ForeignKey
#     les_sections_de_la_page = PageSection.objects.filter(page=page).select_related('section').order_by('order')

#     # On récupère directement les objets sections
#     sections = [ps.section for ps in les_sections_de_la_page]

#     lg = request.LANGUAGE_CODE
#     settings = get_setting()

#     context = {
#         'lg': lg,
#         'settings': settings,
#         'home_page': (page_slug == 'home'),  # Plus pythonique
#         'lessections': {},
#         'section_contents': {},
#         'sections': sections,
#     }

#     # Boucle sur les sections
#     for section in sections:
#         view = section.view

#         if view == 'slider':
#             context['sliders'] = get_slider(lg)
#         elif view == 'breadcrump':
#             context['breadcrump'] = get_breadcrump(page, lg, settings)
#         else:
#             # Optimisation : section est déjà un objet, pas besoin de refaire Section.objects.filter(id=section.id)
#             # J'ai adapté make_data pour qu'il prenne une liste contenant l'objet section
#             context['lessections'][view] = make_data([section], lg)[0]

#             func_name = snake(view)
#             name = f"{func_name}s"
#             the_fonction = f"get_{func_name}"

#             # Vérification et appel dynamique de fonction
#             if the_fonction in globals() and inspect.isfunction(globals()[the_fonction]):
#                 args = []
#                 kwargs = {'lg': lg}
#                 context[name] = call_function(the_fonction, *args, **kwargs)

#         # Récupérer les contenus personnalisés (PageSectionContent)
#         # Note : Assurez-vous que vos méthodes get_section_contents sont optimisées
#         section_contents = page.get_section_contents(section, lg)
#         if section_contents.exists():
#             context['section_contents'][section.id] = {
#                 'before': page.get_section_content_by_position(section, 'before', lg),
#                 'after': page.get_section_content_by_position(section, 'after', lg),
#                 'replace': page.get_section_content_by_position(section, 'replace', lg),
#             }

#     # Remplissage final du contexte
#     context.update({
#         'dictionnaire': get_translator(lg),
#         'menus': get_front_menu(lg),
#         'get_all_contents': get_all_content(lg),
#         'get_all_html_contents': get_all_content_html(lg),
#         'contents': get_content(lg),
#         'data_sections': make_data(sections, lg),
#         'pages': make_data([page], lg)[0],  # Modifié pour éviter une nouvelle requête DB
#         'page': page,  # Accès direct à l'objet page pour le template
#         'theme': page.template.directory,  # Chemin du thème pour les sections
#         'template': page.template.get_template_file_path
#     })

#     return render(request, 'website/page.html', context)


# =============================================================================
# Endpoints HTMX pour le reordonnancement
# =============================================================================

@staff_member_required
@require_POST
@csrf_protect
def reorder_page_sections(request, page_id):
    """
    Endpoint HTMX pour reordonner les sections d'une page.

    Body JSON attendu: {"order": [section_id_1, section_id_2, ...]}
    """
    page = get_object_or_404(Page, pk=page_id)

    try:
        data = json.loads(request.body)
        order_ids = data.get('order', [])

        for index, section_id in enumerate(order_ids):
            PageSection.objects.filter(
                page=page,
                section_id=section_id
            ).update(order=index)

        return JsonResponse({
            'status': 'success',
            'message': 'Sections reordonnees avec succes'
        })
    except json.JSONDecodeError:
        return JsonResponse({
            'status': 'error',
            'message': 'JSON invalide'
        }, status=400)
    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': str(e)
        }, status=500)


@staff_member_required
@require_POST
@csrf_protect
def reorder_menu_items(request, menu_id):
    """
    Endpoint HTMX pour reordonner les elements d'un menu.
    L'ordre est relatif au parent (chaque groupe de freres a sa propre sequence).

    Body JSON attendu: {
        "order": [item_id_1, item_id_2, ...],
        "parent_id": "123" ou null pour les elements racine
    }
    """
    from django.core.cache import cache
    from traduction.models import Language

    menu = get_object_or_404(Menu, pk=menu_id)

    try:
        data = json.loads(request.body)
        order_ids = data.get('order', [])
        parent_id = data.get('parent_id')

        # Convertir parent_id en int ou None
        if parent_id == '' or parent_id is None:
            parent_id = None
        else:
            parent_id = int(parent_id)

        # Mettre a jour l'ordre de chaque element (relatif a ses freres)
        for index, item_id in enumerate(order_ids):
            MenuItem.objects.filter(
                id=item_id,
                menu=menu,
                parent_id=parent_id
            ).update(order=index)

        # Invalider le cache du menu (update() ne declenche pas les signaux)
        cache.delete(f'cms_menu_{menu.slug}')

        return JsonResponse({
            'status': 'success',
            'message': 'Elements reordonnes avec succes'
        })
    except json.JSONDecodeError:
        return JsonResponse({
            'status': 'error',
            'message': 'JSON invalide'
        }, status=400)
    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': str(e)
        }, status=500)


@staff_member_required
@require_POST
@csrf_protect
def reorder_children(request, parent_id):
    """
    Endpoint HTMX pour reordonner les sous-elements d'un menu item.

    Body JSON attendu: {"order": [item_id_1, item_id_2, ...]}
    """
    parent = get_object_or_404(MenuItem, pk=parent_id)

    try:
        data = json.loads(request.body)
        order_ids = data.get('order', [])

        for index, item_id in enumerate(order_ids):
            MenuItem.objects.filter(
                id=item_id,
                parent=parent
            ).update(order=index)

        return JsonResponse({
            'status': 'success',
            'message': 'Sous-elements reordonnes avec succes'
        })
    except json.JSONDecodeError:
        return JsonResponse({
            'status': 'error',
            'message': 'JSON invalide'
        }, status=400)
    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': str(e)
        }, status=500)


@staff_member_required
@require_POST
@csrf_protect
def reorder_inline(request):
    """
    Endpoint HTMX generique pour reordonner les inlines dans l'admin.

    Body JSON attendu: {
        "model": "pagesection" ou "menuitem",
        "parent_id": 123,
        "order": [id_1, id_2, ...]
    }
    """
    try:
        data = json.loads(request.body)
        model_name = data.get('model')
        parent_id = data.get('parent_id')
        order_ids = data.get('order', [])

        if model_name == 'pagesection':
            for index, ps_id in enumerate(order_ids):
                PageSection.objects.filter(
                    id=ps_id,
                    page_id=parent_id
                ).update(order=index)
        elif model_name == 'menuitem':
            for index, item_id in enumerate(order_ids):
                MenuItem.objects.filter(
                    id=item_id,
                    menu_id=parent_id
                ).update(order=index)
        else:
            return JsonResponse({
                'status': 'error',
                'message': 'Modele non supporte'
            }, status=400)

        return JsonResponse({
            'status': 'success',
            'message': 'Ordre mis a jour'
        })
    except json.JSONDecodeError:
        return JsonResponse({
            'status': 'error',
            'message': 'JSON invalide'
        }, status=400)
    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': str(e)
        }, status=500)


# =============================================================================
# Vues pour le gestionnaire de PageSection
# =============================================================================

@staff_member_required
def pagesection_manager_view(request):
    """
    Vue admin pour gerer les sections par page avec interface 3 colonnes.
    """
    pages = Page.objects.select_related('template').prefetch_related('sections').order_by('name')

    return render(request, 'admin/cms/pagesection_manager.html', {
        'pages': pages,
        'title': 'Gestionnaire de sections par page',
    })


@staff_member_required
@require_GET
def get_page_sections(request, page_id):
    """
    API: Retourne les sections d'une page en JSON.
    """
    page = get_object_or_404(Page, pk=page_id)
    page_sections = PageSection.objects.filter(page=page).select_related(
        'section'
    ).prefetch_related(
        'section__custom_fields',
        'field_values'
    ).order_by('order')

    sections_data = []
    for ps in page_sections:
        total_fields = ps.section.custom_fields.count()
        filled_fields = ps.field_values.exclude(
            text_value__isnull=True,
            integer_value__isnull=True,
            decimal_value__isnull=True,
            boolean_value__isnull=True,
            image_value=''
        ).count()

        sections_data.append({
            'id': ps.id,
            'name': ps.section.name,
            'file': ps.section.file,
            'order': ps.order,
            'fields_total': total_fields,
            'fields_filled': filled_fields,
        })

    return JsonResponse({'sections': sections_data})


@staff_member_required
@require_GET
def get_pagesection_fields_form(request, pagesection_id):
    """
    API: Retourne les champs et valeurs d'un PageSection en JSON.
    Inclut les traductions pour les champs texte.
    """
    from django.urls import reverse
    from django.contrib.contenttypes.models import ContentType
    from .models import SectionField, PageSectionFieldValue
    from traduction.models import Language, Dictionnaire

    ps = get_object_or_404(
        PageSection.objects.select_related('page', 'section'),
        pk=pagesection_id
    )

    section_fields = SectionField.objects.filter(section=ps.section).order_by('order')

    if not section_fields.exists():
        return JsonResponse({
            'has_fields': False,
            'section_admin_url': reverse('admin:cms_section_change', args=[ps.section.id])
        })

    # Recuperer les valeurs existantes
    existing_values = {
        fv.field_id: fv
        for fv in ps.field_values.select_related('field').all()
    }

    # Recuperer les langues actives pour les traductions
    languages = list(Language.get_active_languages().values('code', 'name', 'flag'))
    default_language = Language.get_default()

    # Preparer le ContentType pour PageSectionFieldValue
    content_type = ContentType.objects.get_for_model(PageSectionFieldValue)

    # Recuperer toutes les traductions pour les valeurs existantes
    field_value_ids = [fv.id for fv in existing_values.values()]
    translations_qs = Dictionnaire.objects.filter(
        content_type=content_type,
        object_id__in=field_value_ids,
        field='text_value'
    ).select_related('language')

    # Indexer les traductions par (object_id, language_code)
    translations_map = {}
    for t in translations_qs:
        if t.object_id not in translations_map:
            translations_map[t.object_id] = {}
        translations_map[t.object_id][t.language.code] = t.translation

    fields_data = []
    for field in section_fields:
        value = None
        translations = {}
        field_value_id = None

        if field.id in existing_values:
            fv = existing_values[field.id]
            field_value_id = fv.id
            value = fv.get_value()
            # Pour les images, retourner l'URL
            if field.field_type == 'image' and fv.image_value:
                value = fv.image_value.url

            # Recuperer les traductions pour les champs texte
            if field.field_type in ('text', 'textarea', 'url', 'email'):
                if fv.id in translations_map:
                    translations = translations_map[fv.id]

        # Pour les selects, recuperer les choix (convertir tuples en objets)
        choices = []
        if field.field_type == 'select':
            choices = [
                {'value': v, 'label': l}
                for v, l in field.get_choices_list()
            ]

        # Determiner si ce champ est traduisible
        is_translatable = field.field_type in ('text', 'textarea')

        fields_data.append({
            'id': field.id,
            'name': field.name,
            'label': field.label,
            'type': field.field_type,
            'type_display': field.get_field_type_display(),
            'required': field.required,
            'help_text': field.help_text,
            'default_value': field.default_value,
            'value': value,
            'choices': choices,
            'is_translatable': is_translatable,
            'translations': translations,
            'field_value_id': field_value_id,
        })

    return JsonResponse({
        'has_fields': True,
        'page_name': ps.page.name,
        'section_name': ps.section.name,
        'section_file': ps.section.file,
        'fields': fields_data,
        'languages': languages,
        'default_language': default_language.code if default_language else 'fr',
    })


@staff_member_required
@require_POST
@csrf_protect
def save_pagesection_fields(request, pagesection_id):
    """
    API: Sauvegarde les valeurs des champs d'un PageSection.
    Gere egalement les traductions pour les champs texte.
    """
    from decimal import Decimal, InvalidOperation
    from django.contrib.contenttypes.models import ContentType
    from .models import SectionField, PageSectionFieldValue
    from traduction.models import Language, Dictionnaire

    ps = get_object_or_404(PageSection, pk=pagesection_id)

    section_fields = {
        f.id: f for f in SectionField.objects.filter(section=ps.section)
    }

    # Stocker les field_values pour les traductions
    field_values_map = {}

    try:
        for key, value in request.POST.items():
            if not key.startswith('field_'):
                continue

            # Ignorer les traductions pour le moment (traitement separe)
            if '_trans_' in key:
                continue

            field_id = int(key.split('_')[1])
            if field_id not in section_fields:
                continue

            field = section_fields[field_id]

            # Recuperer ou creer la valeur
            field_value, _ = PageSectionFieldValue.objects.get_or_create(
                page_section=ps,
                field=field
            )

            # Traiter selon le type
            if field.field_type == 'boolean':
                field_value.boolean_value = value in ('on', 'true', '1', 'True')
                field_value.text_value = None
                field_value.integer_value = None
                field_value.decimal_value = None
            elif field.field_type == 'integer':
                field_value.integer_value = int(value) if value else None
                field_value.text_value = None
                field_value.boolean_value = None
                field_value.decimal_value = None
            elif field.field_type == 'decimal':
                field_value.decimal_value = Decimal(value) if value else None
                field_value.text_value = None
                field_value.integer_value = None
                field_value.boolean_value = None
            else:
                field_value.text_value = value if value else None
                field_value.integer_value = None
                field_value.boolean_value = None
                field_value.decimal_value = None

            field_value.save()
            field_values_map[field_id] = field_value

        # Traiter les traductions (format: field_{id}_trans_{lang_code})
        content_type = ContentType.objects.get_for_model(PageSectionFieldValue)
        languages = {lang.code: lang for lang in Language.get_active_languages()}

        for key, value in request.POST.items():
            if not key.startswith('field_') or '_trans_' not in key:
                continue

            # Parser: field_{id}_trans_{lang_code}
            parts = key.split('_trans_')
            if len(parts) != 2:
                continue

            field_id = int(parts[0].replace('field_', ''))
            lang_code = parts[1]

            if field_id not in section_fields or lang_code not in languages:
                continue

            field = section_fields[field_id]

            # Seulement pour les champs texte traduisibles
            if field.field_type not in ('text', 'textarea'):
                continue

            # Recuperer ou creer le PageSectionFieldValue
            if field_id in field_values_map:
                field_value = field_values_map[field_id]
            else:
                field_value, _ = PageSectionFieldValue.objects.get_or_create(
                    page_section=ps,
                    field=field
                )
                field_values_map[field_id] = field_value

            # Sauvegarder ou supprimer la traduction
            value = value.strip()
            if value:
                Dictionnaire.set_translation(field_value, 'text_value', languages[lang_code], value)
            else:
                # Supprimer la traduction si vide
                Dictionnaire.objects.filter(
                    content_type=content_type,
                    object_id=field_value.pk,
                    field='text_value',
                    language=languages[lang_code]
                ).delete()

        # Traiter les fichiers (images)
        for key, file in request.FILES.items():
            if not key.startswith('field_'):
                continue

            field_id = int(key.split('_')[1])
            if field_id not in section_fields:
                continue

            field = section_fields[field_id]
            if field.field_type != 'image':
                continue

            field_value, _ = PageSectionFieldValue.objects.get_or_create(
                page_section=ps,
                field=field
            )
            field_value.image_value = file
            field_value.save()

        return JsonResponse({
            'success': True,
            'message': 'Champs enregistres avec succes'
        })

    except (ValueError, InvalidOperation) as e:
        return JsonResponse({
            'success': False,
            'message': f'Erreur de validation: {str(e)}'
        })
    except Exception as e:
        return JsonResponse({
            'success': False,
            'message': f'Erreur: {str(e)}'
        })
