import re

from django.db import models
from django.utils.text import slugify
from django.utils import timezone
from django import forms

from wagtail.models import Page, Orderable
from wagtail.fields import StreamField, RichTextField
from wagtail.images.models import Image
from wagtail.images.blocks import ImageChooserBlock
from wagtail.admin.panels import FieldPanel, MultiFieldPanel, InlinePanel
from wagtail import blocks
from wagtail.blocks import RichTextBlock, TextBlock, StructBlock, CharBlock
from taggit.models import TaggedItemBase
from modelcluster.fields import ParentalKey
from modelcluster.tags import ClusterTaggableManager
from modelcluster.models import ClusterableModel

class CodeBlock(StructBlock):
    language = CharBlock(required=True, help_text='Programming language', default='python')
    code = TextBlock(required=True, help_text='Code snippet')
    
    class Meta:
        template = 'blocks/code_block.html'
        icon = 'code'
        label = 'Code'

class BlogCategory(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True, max_length=100, help_text='URL-friendly name, auto-generated from the title if left blank')
    description = models.TextField(blank=True, help_text='Optional description of this category')
    
    panels = [
        FieldPanel('name'),
        FieldPanel('slug'),
        FieldPanel('description'),
    ]
    
    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)
    
    def __str__(self):
        return self.name
    
    class Meta:
        verbose_name = 'Blog Category'
        verbose_name_plural = 'Blog Categories'
        ordering = ['name']

class BlogIndexPage(Page):
    intro = models.TextField(blank=True)
    subtitle = models.CharField(max_length=250, blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('intro'),
        FieldPanel('subtitle'),
    ]
    
    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        
        # Get blog posts
        blog_posts = BlogPostPage.objects.live().order_by('-published_date')
        
        # Filter by category
        category = request.GET.get('category')
        if category:
            blog_posts = blog_posts.filter(categories__category__slug=category)
        
        # We've removed the search functionality as requested
        # but kept the code structure for future reference if needed
        
        # Pagination
        from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
        paginator = Paginator(blog_posts, 6)  # Show 6 posts per page
        page = request.GET.get('page')
        try:
            blog_posts = paginator.page(page)
        except PageNotAnInteger:
            # If page is not an integer, deliver first page
            blog_posts = paginator.page(1)
        except EmptyPage:
            # If page is out of range, deliver last page of results
            blog_posts = paginator.page(paginator.num_pages)
            
        context['blog_posts'] = blog_posts
        context['categories'] = BlogCategory.objects.all()
        
        return context

class BlogPostPage(Page):
    intro = models.CharField(max_length=250)
    thumbnail = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='blog_thumbnails',
    )
    author = models.CharField(max_length=100)
    author_avatar = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='blog_author_avatars',
        help_text='Author profile picture. If not set, the profile image from the home page will be used.'
    )
    published_date = models.DateField("Post date")
    body = StreamField([
        ("heading", blocks.CharBlock(classname="full title")),
        ("paragraph", blocks.RichTextBlock()),
        ("image", ImageChooserBlock()),
        ("code", CodeBlock()),
    ], use_json_field=True)
    tags = ClusterTaggableManager(through='blogs.BlogPageTag', blank=True)
    estimated_reading_time = models.PositiveIntegerField(default=1)
    views_count = models.PositiveIntegerField(default=0)
    show_table_of_contents = models.BooleanField(default=True, help_text="Show table of contents on the side")
    social_post_url = models.URLField(blank=True, help_text="URL to a social media post where readers can comment on this article")
    SOCIAL_PLATFORM_CHOICES = [
        ('twitter', 'Twitter/X'),
        ('linkedin', 'LinkedIn'),
        ('facebook', 'Facebook'),
        ('github', 'GitHub'),
        ('reddit', 'Reddit'),
        ('other', 'Other Platform')
    ]
    social_platform = models.CharField(
        max_length=20,
        choices=SOCIAL_PLATFORM_CHOICES,
        default='twitter',
        blank=True,
        help_text="Select the social media platform for the post URL"
    )

    content_panels = Page.content_panels + [
        FieldPanel('intro'),
        FieldPanel('thumbnail'),
        FieldPanel('author'),
        FieldPanel('author_avatar'),
        FieldPanel('published_date'),
        InlinePanel('categories', label="Categories"),
        FieldPanel('body'),
        FieldPanel('tags'),
        FieldPanel('estimated_reading_time'),
        FieldPanel('show_table_of_contents'),
        MultiFieldPanel([
            FieldPanel('social_post_url'),
            FieldPanel('social_platform'),
        ], heading="Social Media Comments"),
    ]
    
    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        
        try:
            # Increment view count
            self.views_count += 1
            self.save()
            
            # Get default author avatar from home page if not set
            if not self.author_avatar:
                from home.models import HomePage
                home_page = HomePage.objects.first()
                if home_page and home_page.profile_image:
                    context['default_author_avatar'] = home_page.profile_image
        except Exception as e:
            # Log the error but don't break the page
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f'Error in BlogPostPage.get_context: {str(e)}')
        
        # Get table of contents
        if self.show_table_of_contents:
            toc = []
            has_headings = False
            
            # Regular expressions to find headings in paragraph blocks
            h2_pattern = re.compile(r'<h2[^>]*>(.*?)</h2>', re.DOTALL)
            h3_pattern = re.compile(r'<h3[^>]*>(.*?)</h3>', re.DOTALL)
            h4_pattern = re.compile(r'<h4[^>]*>(.*?)</h4>', re.DOTALL)
            h5_pattern = re.compile(r'<h5[^>]*>(.*?)</h5>', re.DOTALL)
            h6_pattern = re.compile(r'<h6[^>]*>(.*?)</h6>', re.DOTALL)
            
            # Check all blocks for headings
            for block in self.body:
                # Check for dedicated heading blocks
                if block.block_type == 'heading':
                    has_headings = True
                    slug = slugify(block.value)
                    
                    # Determine heading level (default to h2 if not specified)
                    heading_level = getattr(block, 'heading_level', 2)
                    if isinstance(heading_level, str) and heading_level.startswith('h'):
                        # Convert 'h2', 'h3', etc. to integers 2, 3, etc.
                        try:
                            heading_level = int(heading_level[1:])
                        except (ValueError, IndexError):
                            heading_level = 2
                    
                    # Create TOC item with level information
                    toc.append({
                        'title': block.value,
                        'slug': slug,
                        'level': heading_level
                    })
                
                # Check for headings inside paragraph blocks
                elif block.block_type == 'paragraph':
                    # Convert block value to string if it's not already
                    paragraph_html = str(block.value)
                    
                    # Check for h2 tags
                    for match in h2_pattern.finditer(paragraph_html):
                        has_headings = True
                        heading_text = re.sub('<.*?>', '', match.group(1)).strip()  # Remove any HTML tags inside heading
                        slug = slugify(heading_text)
                        toc.append({
                            'title': heading_text,
                            'slug': slug,
                            'level': 2
                        })
                    
                    # Check for h3 tags
                    for match in h3_pattern.finditer(paragraph_html):
                        has_headings = True
                        heading_text = re.sub('<.*?>', '', match.group(1)).strip()
                        slug = slugify(heading_text)
                        toc.append({
                            'title': heading_text,
                            'slug': slug,
                            'level': 3
                        })
                    
                    # Check for h4 tags
                    for match in h4_pattern.finditer(paragraph_html):
                        has_headings = True
                        heading_text = re.sub('<.*?>', '', match.group(1)).strip()
                        slug = slugify(heading_text)
                        toc.append({
                            'title': heading_text,
                            'slug': slug,
                            'level': 4
                        })
                    
                    # Check for h5 tags
                    for match in h5_pattern.finditer(paragraph_html):
                        has_headings = True
                        heading_text = re.sub('<.*?>', '', match.group(1)).strip()
                        slug = slugify(heading_text)
                        toc.append({
                            'title': heading_text,
                            'slug': slug,
                            'level': 5
                        })
                    
                    # Check for h6 tags
                    for match in h6_pattern.finditer(paragraph_html):
                        has_headings = True
                        heading_text = re.sub('<.*?>', '', match.group(1)).strip()
                        slug = slugify(heading_text)
                        toc.append({
                            'title': heading_text,
                            'slug': slug,
                            'level': 6
                        })
            
            # Set context variables
            context['table_of_contents'] = toc
            context['show_toc'] = True
            context['has_headings'] = has_headings
            
            # If no headings exist, add default ones for the TOC
            if not has_headings:
                context['default_toc'] = [
                    {'title': 'Introduction', 'slug': 'introduction', 'level': 2},
                    {'title': 'Main Content', 'slug': 'main-content', 'level': 2},
                    {'title': 'Conclusion', 'slug': 'conclusion', 'level': 2}
                ]
                # Use these for the actual TOC if no real headings exist
                if not toc:
                    context['table_of_contents'] = context['default_toc']
        
        # Get related posts
        related_posts = BlogPostPage.objects.live().exclude(id=self.id)
        if self.tags.all():
            related_posts = related_posts.filter(tags__in=self.tags.all()).distinct()[:3]
        else:
            related_posts = related_posts.order_by('-published_date')[:3]
        context['related_posts'] = related_posts
        
        return context

class BlogPageTag(TaggedItemBase):
    content_object = ParentalKey(
        'BlogPostPage', related_name='tagged_items', on_delete=models.CASCADE
    )

class BlogPageCategory(models.Model):
    page = ParentalKey(
        'BlogPostPage', on_delete=models.CASCADE, related_name='categories'
    )
    category = models.ForeignKey(
        'BlogCategory', on_delete=models.CASCADE, related_name='blog_pages'
    )
    
    panels = [
        FieldPanel('category'),
    ]
    
    class Meta:
        unique_together = ('page', 'category')
        verbose_name = 'Blog Category'
        verbose_name_plural = 'Blog Categories'
    
    def save(self, *args, **kwargs):
        # Check if this category is already assigned to this page
        if not self.pk and BlogPageCategory.objects.filter(page=self.page, category=self.category).exists():
            # If it exists, don't save and just return silently
            return
        super().save(*args, **kwargs)
    
    def __str__(self):
        return f"{self.category.name} on {self.page.title}"