import json
import subprocess
import tempfile
import os
from pathlib import Path

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Q, Max
from django.http import JsonResponse, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_POST

from apps.core.models import UUIDModel
from apps.organizations.models import Hospital
from .models import Presentation, Slide, PresentationTheme, SlideLayout, PresentationStatus
from .models import ReportTemplate, ReportTemplateSlide
from .services import PresentationGeneratorService, DoctorRatingsReportService
from .data_sources import REPORT_DATA_SOURCES, get_data_source_choices
from .template_generator import TemplateReportGenerator
from .template_parser import ReportTemplateParser


@login_required
def presentation_list(request):
    presentations = Presentation.objects.filter(
        Q(created_by=request.user) | Q(is_shared=True)
    ).select_related('created_by', 'hospital').prefetch_related('slides')

    status_filter = request.GET.get('status', '')
    theme_filter = request.GET.get('theme', '')

    if status_filter:
        presentations = presentations.filter(status=status_filter)
    if theme_filter:
        presentations = presentations.filter(theme=theme_filter)

    presentations = presentations.distinct()

    context = {
        'presentations': presentations,
        'status_filter': status_filter,
        'theme_filter': theme_filter,
        'theme_choices': PresentationTheme.choices,
        'status_choices': PresentationStatus.choices,
        'layout_choices': SlideLayout.choices,
    }
    return render(request, 'presentations/presentation_list.html', context)


@login_required
def presentation_create(request):
    if request.method == 'POST':
        presentation = Presentation.objects.create(
            title=request.POST.get('title', ''),
            subtitle=request.POST.get('subtitle', ''),
            description=request.POST.get('description', ''),
            theme=request.POST.get('theme', PresentationTheme.HEALTHCARE_MODERN),
            presentation_type=request.POST.get('presentation_type', ''),
            created_by=request.user,
            hospital=request.user.hospital if hasattr(request.user, 'hospital') else None,
            presentation_date=request.POST.get('presentation_date') or None,
        )
        messages.success(request, _('Presentation created successfully.'))
        return redirect('presentations:detail', pk=presentation.pk)

    context = {
        'theme_choices': PresentationTheme.choices,
        'layout_choices': SlideLayout.choices,
    }
    return render(request, 'presentations/presentation_form.html', context)


@login_required
def presentation_detail(request, pk):
    presentation = get_object_or_404(
        Presentation.objects.select_related('created_by', 'hospital').prefetch_related('slides'),
        pk=pk,
    )
    slides = presentation.slides.all()

    context = {
        'presentation': presentation,
        'slides': slides,
        'layout_choices': SlideLayout.choices,
    }
    return render(request, 'presentations/presentation_detail.html', context)


@login_required
def presentation_edit(request, pk):
    presentation = get_object_or_404(Presentation, pk=pk, created_by=request.user)

    if request.method == 'POST':
        presentation.title = request.POST.get('title', presentation.title)
        presentation.subtitle = request.POST.get('subtitle', '')
        presentation.description = request.POST.get('description', '')
        presentation.theme = request.POST.get('theme', presentation.theme)
        presentation.status = request.POST.get('status', presentation.status)
        presentation.presentation_type = request.POST.get('presentation_type', '')
        presentation.is_shared = request.POST.get('is_shared') == 'on'
        presentation.presentation_date = request.POST.get('presentation_date') or None
        presentation.save()
        messages.success(request, _('Presentation updated.'))
        return redirect('presentations:detail', pk=presentation.pk)

    context = {
        'presentation': presentation,
        'theme_choices': PresentationTheme.choices,
        'status_choices': PresentationStatus.choices,
    }
    return render(request, 'presentations/presentation_form.html', context)


@login_required
@require_POST
def presentation_delete(request, pk):
    presentation = get_object_or_404(Presentation, pk=pk, created_by=request.user)
    presentation.delete()
    messages.success(request, _('Presentation deleted.'))
    return redirect('presentations:list')


@login_required
def presentation_view(request, pk):
    presentation = get_object_or_404(
        Presentation.objects.select_related('created_by', 'hospital').prefetch_related('slides'),
        pk=pk,
    )
    slides = presentation.slides.all()

    rendered_slides = []
    for slide in slides:
        try:
            rendered = render_to_string(slide.template_name, {
                'slide': slide,
                'presentation': presentation,
                'slide_index': slide.order,
                'total_slides': slides.count(),
                'request': request,
            })
            rendered_slides.append({
                'id': str(slide.id),
                'html': rendered,
                'layout': slide.layout,
                'title': slide.title,
                'notes': slide.speaker_notes,
            })
        except Exception:
            rendered_slides.append({
                'id': str(slide.id),
                'html': f'<div style="padding:80px;text-align:center;color:#64748b;">Slide {slide.order}: {slide.get_layout_display()}</div>',
                'layout': slide.layout,
                'title': slide.title,
                'notes': '',
            })

    context = {
        'presentation': presentation,
        'rendered_slides': rendered_slides,
        'slides': slides,
    }
    return render(request, 'presentations/presentation_view.html', context)


@login_required
def slide_add(request, pk):
    presentation = get_object_or_404(Presentation, pk=pk, created_by=request.user)

    if request.method == 'POST':
        max_order = presentation.slides.aggregate(models.Max('order'))['order__max'] or 0
        slide = Slide.objects.create(
            presentation=presentation,
            layout=request.POST.get('layout', SlideLayout.COVER),
            order=max_order + 1,
            title=request.POST.get('title', ''),
            subtitle=request.POST.get('subtitle', ''),
            content=json.loads(request.POST.get('content', '{}')),
            speaker_notes=request.POST.get('speaker_notes', ''),
        )
        messages.success(request, _('Slide added.'))
        return redirect('presentations:detail', pk=presentation.pk)

    context = {
        'presentation': presentation,
        'layout_choices': SlideLayout.choices,
    }
    return render(request, 'presentations/slide_form.html', context)


@login_required
def slide_edit(request, pk, slide_id):
    presentation = get_object_or_404(Presentation, pk=pk, created_by=request.user)
    slide = get_object_or_404(Slide, pk=slide_id, presentation=presentation)

    if request.method == 'POST':
        slide.layout = request.POST.get('layout', slide.layout)
        slide.title = request.POST.get('title', '')
        slide.subtitle = request.POST.get('subtitle', '')
        slide.content = json.loads(request.POST.get('content', '{}'))
        slide.speaker_notes = request.POST.get('speaker_notes', '')
        slide.background_color = request.POST.get('background_color', '')
        slide.save()
        messages.success(request, _('Slide updated.'))
        return redirect('presentations:detail', pk=presentation.pk)

    context = {
        'presentation': presentation,
        'slide': slide,
        'layout_choices': SlideLayout.choices,
    }
    return render(request, 'presentations/slide_form.html', context)


@login_required
@require_POST
def slide_delete(request, pk, slide_id):
    presentation = get_object_or_404(Presentation, pk=pk, created_by=request.user)
    slide = get_object_or_404(Slide, pk=slide_id, presentation=presentation)
    slide.delete()

    for i, s in enumerate(presentation.slides.order_by('order'), 1):
        s.order = i
        s.save(update_fields=['order'])

    messages.success(request, _('Slide deleted.'))
    return redirect('presentations:detail', pk=presentation.pk)


@login_required
@require_POST
def slides_reorder(request, pk):
    presentation = get_object_or_404(Presentation, pk=pk, created_by=request.user)
    order_data = json.loads(request.POST.get('order', '[]'))

    for item in order_data:
        Slide.objects.filter(
            pk=item.get('id'),
            presentation=presentation,
        ).update(order=item.get('order', 0))

    return JsonResponse({'status': 'ok'})


@login_required
def export_pdf(request, pk):
    presentation = get_object_or_404(Presentation, pk=pk)
    slides = presentation.slides.all()

    rendered_pages = []
    for slide in slides:
        try:
            rendered = render_to_string(slide.template_name, {
                'slide': slide,
                'presentation': presentation,
                'slide_index': slide.order,
                'total_slides': slides.count(),
                'request': request,
                'for_export': True,
            })
            rendered_pages.append(rendered)
        except Exception:
            rendered_pages.append(
                f'<div style="width:1920px;height:1080px;padding:80px;">Slide {slide.order}</div>'
            )

    html_content = render_to_string('presentations/export_pdf.html', {
        'presentation': presentation,
        'pages': rendered_pages,
    })

    response = HttpResponse(html_content, content_type='text/html')
    response['Content-Disposition'] = f'attachment; filename="{presentation.title}.html"'
    return response


@login_required
def export_pptx(request, pk):
    presentation = get_object_or_404(Presentation, pk=pk)
    slides = presentation.slides.all()

    slide_data = []
    for slide in slides:
        slide_data.append({
            'layout': slide.layout,
            'title': slide.title,
            'subtitle': slide.subtitle,
            'content': slide.content,
            'order': slide.order,
        })

    data = {
        'title': presentation.title,
        'subtitle': presentation.subtitle,
        'theme': presentation.theme,
        'slides': slide_data,
    }

    response = JsonResponse(data)
    response['Content-Disposition'] = f'attachment; filename="{presentation.title}.json"'
    return response


@login_required
def presentation_generate(request):
    hospitals = Hospital.objects.filter(status='active').order_by('name')

    if request.method == 'POST':
        hospital_id = request.POST.get('hospital')
        year = int(request.POST.get('year', timezone.now().year))
        quarter = request.POST.get('quarter')
        quarter = int(quarter) if quarter else None

        if not hospital_id:
            messages.error(request, _('Please select a hospital.'))
        else:
            try:
                report_type = request.POST.get('report_type', 'complaints')
                if report_type == 'doctor_ratings':
                    service = DoctorRatingsReportService(
                        hospital_id=hospital_id,
                        year=year,
                        quarter=quarter,
                        created_by=request.user,
                    )
                else:
                    service = PresentationGeneratorService(
                        hospital_id=hospital_id,
                        year=year,
                        quarter=quarter,
                        created_by=request.user,
                    )
                presentation = service.generate()
                messages.success(request, _('Presentation generated successfully with %d slides.') % presentation.slide_count)
                return redirect('presentations:detail', pk=presentation.pk)
            except Exception as e:
                messages.error(request, _('Error generating presentation: %s') % str(e))

    current_year = timezone.now().year
    years = list(range(current_year, current_year - 5, -1))

    context = {
        'hospitals': hospitals,
        'years': years,
        'selected_hospital': request.POST.get('hospital'),
        'selected_year': int(request.POST.get('year', current_year)),
        'selected_quarter': request.POST.get('quarter'),
        'selected_report_type': request.POST.get('report_type', 'complaints'),
    }
    return render(request, 'presentations/presentation_generate.html', context)


# ── Report Template Views ─────────────────────────────────────

@login_required
def template_list(request):
    templates = ReportTemplate.objects.select_related('created_by', 'hospital').prefetch_related('template_slides')
    context = {
        'templates': templates,
        'data_sources': REPORT_DATA_SOURCES,
    }
    return render(request, 'presentations/template_list.html', context)


@login_required
def template_create(request):
    if request.method == 'POST':
        name = request.POST.get('name', '')
        slug = request.POST.get('slug', '')
        data_source = request.POST.get('data_source', '')
        description = request.POST.get('description', '')

        template = ReportTemplate.objects.create(
            name=name,
            slug=slug,
            data_source=data_source,
            description=description,
            created_by=request.user,
            hospital_id=request.POST.get('hospital') or None,
        )

        if request.FILES.get('reference_pdf'):
            template.reference_pdf = request.FILES['reference_pdf']
            template.save()

            try:
                parser = ReportTemplateParser(template)
                parser.parse()
                messages.success(request, _('Template created and PDF parsed successfully.'))
            except Exception as e:
                messages.warning(request, _('Template created but PDF parsing failed: %s') % str(e))
        else:
            messages.success(request, _('Template created. Upload a reference PDF to auto-generate slides.'))

        return redirect('presentations:template_detail', pk=template.pk)

    context = {
        'data_source_choices': get_data_source_choices(),
        'data_sources': REPORT_DATA_SOURCES,
    }
    return render(request, 'presentations/template_create.html', context)


@login_required
def template_detail(request, pk):
    template = get_object_or_404(ReportTemplate.objects.prefetch_related('template_slides'), pk=pk)
    template_slides = template.template_slides.order_by('order')

    context = {
        'template': template,
        'template_slides': template_slides,
        'layout_choices': SlideLayout.choices,
        'data_source_info': REPORT_DATA_SOURCES.get(template.data_source, {}),
    }
    return render(request, 'presentations/template_detail.html', context)


@login_required
@require_POST
def template_delete(request, pk):
    template = get_object_or_404(ReportTemplate, pk=pk, created_by=request.user)
    template.delete()
    messages.success(request, _('Template deleted.'))
    return redirect('presentations:template_list')


@login_required
@require_POST
def template_parse(request, pk):
    template = get_object_or_404(ReportTemplate, pk=pk)

    if not template.reference_pdf:
        return JsonResponse({'status': 'error', 'message': 'No reference PDF uploaded'}, status=400)

    try:
        parser = ReportTemplateParser(template)
        parser.parse()
        slide_count = template.template_slides.count()
        return JsonResponse({
            'status': 'ok',
            'slide_count': slide_count,
            'message': f'Parsed successfully. {slide_count} slides created.',
        })
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)}, status=500)


@login_required
@require_POST
def template_slide_create(request, pk):
    template = get_object_or_404(ReportTemplate, pk=pk)
    max_order = template.template_slides.aggregate(Max('order'))['order__max'] or 0

    slide = ReportTemplateSlide.objects.create(
        template=template,
        order=max_order + 1,
        layout=request.POST.get('layout', SlideLayout.COVER),
        title_template=request.POST.get('title_template', ''),
        subtitle_template=request.POST.get('subtitle_template', ''),
        content_mapping=json.loads(request.POST.get('content_mapping', '{}')),
        repeat_source=request.POST.get('repeat_source', ''),
        max_rows=int(request.POST.get('max_rows', 18)),
    )

    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        return JsonResponse({
            'status': 'ok',
            'slide': {
                'id': str(slide.id),
                'order': slide.order,
                'layout': slide.layout,
                'title_template': slide.title_template,
            },
        })

    messages.success(request, _('Slide added.'))
    return redirect('presentations:template_detail', pk=template.pk)


@login_required
@require_POST
def template_slide_update(request, pk, slide_id):
    template = get_object_or_404(ReportTemplate, pk=pk)
    slide = get_object_or_404(ReportTemplateSlide, pk=slide_id, template=template)

    slide.layout = request.POST.get('layout', slide.layout)
    slide.section_label = request.POST.get('section_label', '')
    slide.title_template = request.POST.get('title_template', '')
    slide.subtitle_template = request.POST.get('subtitle_template', '')
    slide.content_mapping = json.loads(request.POST.get('content_mapping', '{}'))
    slide.repeat_source = request.POST.get('repeat_source', '')
    slide.repeat_title_key = request.POST.get('repeat_title_key', 'name')
    slide.repeat_subtitle_template = request.POST.get('repeat_subtitle_template', '')
    slide.max_rows = int(request.POST.get('max_rows', 18))
    slide.style_overrides = json.loads(request.POST.get('style_overrides', '{}'))
    slide.speaker_notes_template = request.POST.get('speaker_notes_template', '')
    slide.save()

    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        return JsonResponse({'status': 'ok'})

    messages.success(request, _('Slide updated.'))
    return redirect('presentations:template_detail', pk=template.pk)


@login_required
@require_POST
def template_slide_delete(request, pk, slide_id):
    template = get_object_or_404(ReportTemplate, pk=pk)
    slide = get_object_or_404(ReportTemplateSlide, pk=slide_id, template=template)
    slide.delete()

    for i, s in enumerate(template.template_slides.order_by('order'), 1):
        s.order = i
        s.save(update_fields=['order'])

    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        return JsonResponse({'status': 'ok'})

    messages.success(request, _('Slide deleted.'))
    return redirect('presentations:template_detail', pk=template.pk)


@login_required
@require_POST
def template_slide_reorder(request, pk):
    template = get_object_or_404(ReportTemplate, pk=pk)
    order_data = json.loads(request.POST.get('order', '[]'))

    for item in order_data:
        ReportTemplateSlide.objects.filter(
            pk=item.get('id'),
            template=template,
        ).update(order=item.get('order', 0))

    return JsonResponse({'status': 'ok'})


@login_required
def template_generate(request, pk):
    template = get_object_or_404(ReportTemplate, pk=pk, active=True)

    if request.method == 'POST':
        hospital_id = request.POST.get('hospital')
        year = int(request.POST.get('year', timezone.now().year))
        quarter = request.POST.get('quarter')
        quarter = int(quarter) if quarter else None

        if not hospital_id:
            messages.error(request, _('Please select a hospital.'))
        else:
            try:
                generator = TemplateReportGenerator(
                    template_id=template.pk,
                    hospital_id=hospital_id,
                    year=year,
                    quarter=quarter,
                    created_by=request.user,
                )
                presentation = generator.generate()
                messages.success(request, _('Report generated with %d slides.') % presentation.slide_count)
                return redirect('presentations:detail', pk=presentation.pk)
            except Exception as e:
                messages.error(request, _('Error generating report: %s') % str(e))

    hospitals = Hospital.objects.filter(status='active').order_by('name')
    current_year = timezone.now().year
    years = list(range(current_year, current_year - 5, -1))

    context = {
        'template': template,
        'hospitals': hospitals,
        'years': years,
    }
    return render(request, 'presentations/template_generate.html', context)
