from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
from django.urls import reverse_lazy
from django.http import JsonResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.contrib import messages
from django.utils.translation import gettext_lazy as _
from django.core.files.base import ContentFile
from django.conf import settings
from django.core.cache import cache
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .templatetags.tasklist_markdown import update_task_in_markdown
from rest_framework.pagination import PageNumberPagination
from .models import Page, Screenshot, Tag
from .forms import PageForm
from .serializers import PageSerializer
import os
import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse, quote
import re
import base64
from .tasks import capture_screenshot, process_page, fetch_webpage_content_from_crawl4ai
from pathlib import Path
from datetime import datetime
from rest_framework.decorators import action
from threading import Thread


def trigger_page_processing(page):
    """
    Trigger background tasks for a newly created page:
    1. Screenshot capture
    2. Crawl4AI content extraction
    3. Page metadata processing

    This is called whenever a page is created, regardless of the source (UI or API).
    """
    from .tasks import capture_screenshot, fetch_webpage_content_from_crawl4ai, process_page

    # Create screenshot record and trigger capture
    screenshot = Screenshot.objects.create(
        page=page,
        status=Screenshot.Status.PENDING
    )

    # Start screenshot capture thread
    thread_screenshot = Thread(target=capture_screenshot, args=(page.id, screenshot.id))
    thread_screenshot.daemon = True
    thread_screenshot.start()

    # Start Crawl4AI content extraction thread
    thread_crawl = Thread(target=fetch_webpage_content_from_crawl4ai, args=(page.id,))
    thread_crawl.daemon = True
    thread_crawl.start()

    # Start page metadata processing thread (if needed)
    if not page.title or not page.summary:
        thread_process = Thread(target=process_page, args=(page.id,))
        thread_process.daemon = True
        thread_process.start()


class PageListView(ListView):
    model = Page
    template_name = 'links/page_list.html'
    context_object_name = 'pages'
    paginate_by = 10

    def get_template_names(self):
        if self.request.headers.get('HX-Request'):
            return ['links/includes/page_list_items.html']
        return [self.template_name]

class PageDetailView(DetailView):
    model = Page
    template_name = 'links/page_detail.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['all_tags'] = Tag.objects.all()
        return context

class PageCreateView(CreateView):
    model = Page
    form_class = PageForm
    template_name = 'links/page_form.html'
    success_url = reverse_lazy('page-list')

    def form_valid(self, form):
        response = super().form_valid(form)
        page = self.object

        # Trigger all background processing tasks
        try:
            trigger_page_processing(page)
            messages.success(self.request, _('Page created successfully and processing started.'))
        except Exception as e:
            messages.error(self.request, _('Page created but processing failed to start.'))
            page.error_message = str(e)
            page.save()

        return response

class PageUpdateView(UpdateView):
    model = Page
    form_class = PageForm
    template_name = 'links/page_form.html'
    success_url = reverse_lazy('page-list')

    def dispatch(self, request, *args, **kwargs):
        response = super().dispatch(request, *args, **kwargs)
        # Prevent caching to ensure fresh markdown content after task toggles
        response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
        response['Pragma'] = 'no-cache'
        response['Expires'] = '0'
        return response

    def form_valid(self, form):
        response = super().form_valid(form)

        # Check if reset_status was checked
        if self.request.POST.get('reset_status'):
            self.object.process_status = Page.ProcessStatus.PENDING
            self.object.retry_count = 0
            self.object.error_message = ''
            self.object.screenshot_path = ''
            self.object.save()

            # Trigger processing task
            from .tasks import process_page
            thread = Thread(target=process_page, args=(self.object.id,))
            thread.daemon = True
            thread.start()

        return response

class PageDeleteView(DeleteView):
    model = Page
    template_name = 'links/page_confirm_delete.html'
    success_url = reverse_lazy('page-list')

def fetch_page_info(request):
    url = request.GET.get('url')
    if not url:
        return JsonResponse({'error': 'URL is required'}, status=400)

    url = url.lstrip('@')

    # Return cached metadata if available (24-hour TTL)
    import hashlib as _hashlib
    cache_key = 'pageinfo:' + _hashlib.md5(url.encode()).hexdigest()
    cached = cache.get(cache_key)
    if cached is not None:
        return JsonResponse(cached)

    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.5',
        }

        response = requests.get(url, headers=headers, timeout=5, verify=False)  # Reduced timeout to 5 seconds
        if response.encoding == 'ISO-8859-1':
            response.encoding = response.apparent_encoding or 'utf-8'

        soup = BeautifulSoup(response.text, 'html.parser')

        # Get title
        title = None
        if soup.title:
            title = soup.title.string
        elif soup.find('h1'):
            title = soup.find('h1').get_text(strip=True)
        elif soup.find('meta', property='og:title'):
            title = soup.find('meta', property='og:title').get('content')

        if title:
            title = re.sub(r'\s+', ' ', title.strip())
            title = title.replace(' | ', ' - ').replace(' :: ', ' - ')
        else:
            title = urlparse(url).netloc

        # Get description
        description = None
        meta_desc = soup.find('meta', {'name': 'description'}) or soup.find('meta', {'property': 'og:description'})
        if meta_desc:
            description = meta_desc.get('content')

        if not description:
            for tag in soup(['script', 'style', 'nav', 'header', 'footer']):
                tag.decompose()

            paragraphs = soup.find_all(['p', 'div'])
            for p in paragraphs:
                text = p.get_text(strip=True)
                if len(text) > 100:
                    description = text
                    break

        if not description:
            description = title

        description = re.sub(r'\s+', ' ', description.strip())
        description = description[:500] + '...' if len(description) > 500 else description

        result = {'title': title, 'summary': description}
        cache.set(cache_key, result, timeout=86400)
        return JsonResponse(result)

    except Exception as e:
        return JsonResponse({
            'error': f'Error processing page: {str(e)}'
        }, status=400)

class StandardResultsSetPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 100

class PageViewSet(viewsets.ModelViewSet):
    queryset = Page.objects.all().order_by('-created_at')
    serializer_class = PageSerializer
    pagination_class = StandardResultsSetPagination

    def get_queryset(self):
        return Page.objects.all().order_by('-created_at')

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        instance = serializer.save()
        if instance.title and instance.summary:
            instance.process_status = Page.ProcessStatus.COMPLETED
        else:
            instance.process_status = Page.ProcessStatus.PENDING
        instance.save()

        # Trigger all background processing tasks
        trigger_page_processing(instance)

    @action(detail=True, methods=['post'], url_path='take-screenshot')
    def take_screenshot(self, request, pk=None):
        """API endpoint to take a new screenshot of a page"""
        page = self.get_object()

        # Create new Screenshot instance
        screenshot = Screenshot.objects.create(
            page=page,
            status=Screenshot.Status.PENDING
        )

        # Trigger screenshot capture task
        try:
            thread = Thread(target=capture_screenshot, args=(page.id, screenshot.id))
            thread.daemon = True
            thread.start()
            return Response({
                'status': 'success',
                'message': 'Screenshot capture initiated',
                'screenshot_id': screenshot.id
            })
        except Exception as e:
            screenshot.status = Screenshot.Status.FAILED
            screenshot.error = str(e)
            screenshot.save()
            return Response({
                'status': 'error',
                'error': str(e)
            }, status=400)

    @action(detail=False, methods=['delete'], url_path='screenshots/(?P<screenshot_id>[^/.]+)')
    def delete_screenshot(self, request, screenshot_id=None):
        """Delete a specific screenshot"""
        try:
            screenshot = Screenshot.objects.get(id=screenshot_id)

            # Optional: Add permission check here
            # if screenshot.page.user != request.user:
            #     return Response(status=status.HTTP_403_FORBIDDEN)

            # Delete the actual file
            if screenshot.path:
                file_path = os.path.join(settings.MEDIA_ROOT, screenshot.path)
                try:
                    os.remove(file_path)
                except OSError:
                    pass  # File doesn't exist or can't be deleted

            # Delete the database record
            screenshot.delete()

            return Response(status=status.HTTP_204_NO_CONTENT)

        except Screenshot.DoesNotExist:
            return Response(
                {"error": "Screenshot not found"},
                status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            return Response(
                {"error": str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=True, methods=['post'])
    def toggle_task(self, request, pk=None):
        """
        Toggle the checked state of a task in a page.

        POST /api/pages/{id}/toggle_task/
        Body: {"task_hash": "abc123"}
        """
        page = self.get_object()
        task_hash = request.data.get('task_hash')

        if not task_hash:
            return Response(
                {'error': 'task_hash is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Get current task states or initialize empty dict
        task_states = page.task_states if page.task_states else {}

        # Toggle the state (default to False if not set, then toggle)
        current_state = task_states.get(task_hash, False)
        new_state = not current_state
        task_states[task_hash] = new_state

        # Save only task_states, leave markdown content unchanged
        page.task_states = task_states
        page.save(update_fields=['task_states', 'updated_at'])

        return Response({
            'task_hash': task_hash,
            'checked': new_state
        })

class ScreenshotGalleryView(TemplateView):
    template_name = 'links/screenshot_gallery.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        screenshots_dir = os.path.join(settings.MEDIA_ROOT, 'screenshots')
        screenshots = []

        if os.path.exists(screenshots_dir):
            for filename in os.listdir(screenshots_dir):
                if filename.endswith('.png'):
                    file_path = os.path.join(screenshots_dir, filename)
                    # Extract page ID and clean up filename
                    name_parts = filename.split('_', 2)  # Split into ['page', id, 'rest_of_name']
                    if len(name_parts) >= 2:
                        id = name_parts[1].replace('.png', '')  # Remove .png extension
                    else:
                        id = '1'
                    if len(name_parts) >= 3:
                        display_name = name_parts[2].replace('.png', '')
                    else:
                        display_name = filename.replace('.png', '')

                    screenshots.append({
                        'url': f'/media/screenshots/{filename}',
                        'created': datetime.fromtimestamp(os.path.getctime(file_path)),
                        'display_name': display_name,
                        'id': id
                    })

        # Sort screenshots by creation time, newest first
        screenshots.sort(key=lambda x: x['created'], reverse=True)
        context['screenshots'] = screenshots
        return context

def update_page_tags(request, pk):
    if request.method == 'POST':
        page = get_object_or_404(Page, pk=pk)
        selected_tags = request.POST.getlist('tags')
        page.tags.clear()
        if selected_tags:
            page.tags.add(*selected_tags)
        messages.success(request, _('Tags updated successfully'))
        return HttpResponseRedirect(reverse('page-detail', args=[pk]))
    return HttpResponseRedirect(reverse('page-detail', args=[pk]))
