"""
Full-text search backend using Whoosh
"""
import os
import logging
from django.conf import settings
from whoosh import index
from whoosh.fields import Schema, TEXT, ID, DATETIME, KEYWORD, NUMERIC
from whoosh.qparser import MultifieldParser, OrGroup
from whoosh.analysis import StemmingAnalyzer
from .models import Link, Page, Post, Bookmark

logger = logging.getLogger(__name__)

# Define index directory
INDEX_DIR = os.path.join(settings.BASE_DIR, 'data', 'search_index')

# Define schema for search index
SCHEMA = Schema(
    id=ID(stored=True, unique=True),
    model_type=ID(stored=True),
    title=TEXT(stored=True, analyzer=StemmingAnalyzer()),
    content=TEXT(analyzer=StemmingAnalyzer()),
    url=TEXT(stored=True),
    alias=TEXT(stored=True),
    description=TEXT(stored=True, analyzer=StemmingAnalyzer()),
    summary=TEXT(stored=True, analyzer=StemmingAnalyzer()),
    tags=KEYWORD(stored=True, commas=True, scorable=True),
    created_at=DATETIME(stored=True, sortable=True),
    click_count=NUMERIC(stored=True, sortable=True)
)


class SearchBackend:
    """
    Search backend powered by Whoosh for full-text indexing and searching
    """
    
    def __init__(self):
        self.index_dir = INDEX_DIR
        self.ensure_index_exists()
    
    def ensure_index_exists(self):
        """Create index directory and index if they don't exist"""
        try:
            if not os.path.exists(self.index_dir):
                os.makedirs(self.index_dir, exist_ok=True)
                index.create_in(self.index_dir, SCHEMA)
                logger.info(f"Created new search index at {self.index_dir}")
        except Exception as e:
            logger.error(f"Failed to ensure index exists at {self.index_dir}: {e}")
            # Don't raise - allow app to continue even if search fails
    
    def get_index(self):
        """Get or create the search index"""
        try:
            if not index.exists_in(self.index_dir):
                if not os.path.exists(self.index_dir):
                    os.makedirs(self.index_dir, exist_ok=True)
                return index.create_in(self.index_dir, SCHEMA)
            return index.open_dir(self.index_dir)
        except Exception as e:
            logger.error(f"Failed to get/create search index: {e}")
            raise
    
    def index_link(self, link):
        """Index a single link"""
        ix = self.get_index()
        writer = ix.writer()
        
        try:
            # Get tags as comma-separated string
            tags = ','.join([tag.name for tag in link.tags.all()])
            
            writer.update_document(
                id=f"link_{link.id}",
                model_type="link",
                title=link.alias or "",
                content=link.text or "",
                url=link.original_url or "",
                alias=link.alias or "",
                description=link.description or "",
                tags=tags,
                created_at=link.created_at,
                click_count=link.click_count
            )
            writer.commit()
            logger.debug(f"Indexed link: {link.id}")
        except Exception as e:
            writer.cancel()
            logger.error(f"Error indexing link {link.id}: {e}", exc_info=True)
    
    def index_page(self, page):
        """Index a single page"""
        ix = self.get_index()
        writer = ix.writer()
        
        try:
            # Get tags as comma-separated string
            tags = ','.join([tag.name for tag in page.tags.all()])
            
            writer.update_document(
                id=f"page_{page.id}",
                model_type="page",
                title=page.title or "",
                content=page.content or "",
                url=page.url or "",
                summary=page.summary or "",
                tags=tags,
                created_at=page.created_at
            )
            writer.commit()
            logger.debug(f"Indexed page: {page.id}")
        except Exception as e:
            writer.cancel()
            logger.error(f"Error indexing page {page.id}: {e}", exc_info=True)
    
    def index_post(self, post):
        """Index a single post"""
        ix = self.get_index()
        writer = ix.writer()

        try:
            # Get tags as comma-separated string
            tags = ','.join([tag.name for tag in post.tags.all()])

            writer.update_document(
                id=f"post_{post.id}",
                model_type="post",
                title=post.title or "",
                content=post.content or "",
                summary=post.summary or "",
                tags=tags,
                created_at=post.created_at
            )
            writer.commit()
            logger.debug(f"Indexed post: {post.id}")
        except Exception as e:
            writer.cancel()
            logger.error(f"Error indexing post {post.id}: {e}", exc_info=True)

    def index_bookmark(self, bookmark):
        """Index a single archived social-media bookmark.

        The `title` field holds the author handle + first line of text (so
        keyword search and the result list both surface useful context); the
        `content` field holds the full post text; `url` is the canonical post
        URL; `summary` is any agent-written AI summary.
        """
        ix = self.get_index()
        writer = ix.writer()

        try:
            text = (bookmark.text or '').strip()
            first_line = text.split('\n', 1)[0][:200] if text else ''
            handle = f"@{bookmark.author_screen_name}" if bookmark.author_screen_name else ''
            title = f"{handle} {first_line}".strip() or (bookmark.tweet_id or '')

            writer.update_document(
                id=f"bookmark_{bookmark.id}",
                model_type="bookmark",
                title=title,
                content=text,
                url=bookmark.url or '',
                summary=bookmark.summary or '',
                tags='',
                created_at=bookmark.tweet_created_at or bookmark.created_at
            )
            writer.commit()
            logger.debug(f"Indexed bookmark: {bookmark.id}")
        except Exception as e:
            writer.cancel()
            logger.error(f"Error indexing bookmark {bookmark.id}: {e}", exc_info=True)
    
    def remove_from_index(self, model_type, model_id):
        """Remove a document from the index"""
        ix = self.get_index()
        writer = ix.writer()
        
        try:
            writer.delete_by_term('id', f"{model_type}_{model_id}")
            writer.commit()
            logger.debug(f"Removed {model_type} {model_id} from index")
        except Exception as e:
            writer.cancel()
            logger.error(f"Error removing {model_type} {model_id} from index: {e}", exc_info=True)
    
    def rebuild_index(self):
        """Rebuild the entire search index from scratch"""
        logger.info("Starting search index rebuild...")
        
        # Delete and recreate index
        ix = index.create_in(self.index_dir, SCHEMA)
        writer = ix.writer()
        
        try:
            # Index all links
            for link in Link.objects.all().prefetch_related('tags'):
                tags = ','.join([tag.name for tag in link.tags.all()])
                writer.add_document(
                    id=f"link_{link.id}",
                    model_type="link",
                    title=link.alias or "",
                    content=link.text or "",
                    url=link.original_url or "",
                    alias=link.alias or "",
                    description=link.description or "",
                    tags=tags,
                    created_at=link.created_at,
                    click_count=link.click_count
                )
            
            # Index all pages
            for page in Page.objects.all().prefetch_related('tags'):
                tags = ','.join([tag.name for tag in page.tags.all()])
                writer.add_document(
                    id=f"page_{page.id}",
                    model_type="page",
                    title=page.title or "",
                    content=page.content or "",
                    url=page.url or "",
                    summary=page.summary or "",
                    tags=tags,
                    created_at=page.created_at
                )
            
            # Index all posts
            for post in Post.objects.all().prefetch_related('tags'):
                tags = ','.join([tag.name for tag in post.tags.all()])
                writer.add_document(
                    id=f"post_{post.id}",
                    model_type="post",
                    title=post.title or "",
                    content=post.content or "",
                    summary=post.summary or "",
                    tags=tags,
                    created_at=post.created_at
                )

            # Index all archived bookmarks
            for b in Bookmark.objects.all().iterator():
                text = (b.text or '').strip()
                first_line = text.split('\n', 1)[0][:200] if text else ''
                handle = f"@{b.author_screen_name}" if b.author_screen_name else ''
                title = f"{handle} {first_line}".strip() or (b.tweet_id or '')
                writer.add_document(
                    id=f"bookmark_{b.id}",
                    model_type="bookmark",
                    title=title,
                    content=text,
                    url=b.url or "",
                    summary=b.summary or "",
                    tags="",
                    created_at=b.tweet_created_at or b.created_at
                )

            writer.commit()
            logger.info("Search index rebuild completed successfully")
        except Exception as e:
            writer.cancel()
            logger.error(f"Error rebuilding index: {e}", exc_info=True)
            raise
    
    def search(self, query_string, model_type=None, limit=100, page=1, per_page=20, sort_by='relevance'):
        """
        Search the index with the given query
        
        Args:
            query_string: Search query
            model_type: Filter by model type (link, page, post) or None for all
            limit: Maximum number of results to return
            page: Page number for pagination
            per_page: Results per page
            sort_by: Sort order - 'relevance', 'newest', 'oldest'
            
        Returns:
            dict with results and metadata
        """
        if not query_string.strip():
            return {'results': [], 'total': 0, 'page': page, 'per_page': per_page}
        
        ix = self.get_index()
        
        with ix.searcher() as searcher:
            # Create multifield parser - search across multiple fields
            fields = ['title', 'content', 'url', 'alias', 'description', 'summary', 'tags']
            parser = MultifieldParser(fields, schema=ix.schema, group=OrGroup)
            
            try:
                query = parser.parse(query_string)
            except Exception as e:
                logger.error(f"Error parsing query '{query_string}': {e}")
                return {'results': [], 'total': 0, 'page': page, 'per_page': per_page}
            
            # Apply model type filter if specified
            if model_type:
                from whoosh.query import And, Term
                query = And([query, Term('model_type', model_type)])
            
            # Determine sort order
            sortedby = None
            reverse = True
            if sort_by == 'newest':
                sortedby = 'created_at'
                reverse = True
            elif sort_by == 'oldest':
                sortedby = 'created_at'
                reverse = False
            
            # Execute search
            results = searcher.search(query, limit=limit, sortedby=sortedby, reverse=reverse)
            
            # Extract results with pagination
            start = (page - 1) * per_page
            end = start + per_page
            total = len(results)
            
            result_list = []
            for hit in results[start:end]:
                result_dict = dict(hit)
                # Extract model ID from composite ID
                model_id = result_dict['id'].split('_')[1]
                result_dict['model_id'] = int(model_id)
                result_list.append(result_dict)
            
            return {
                'results': result_list,
                'total': total,
                'page': page,
                'per_page': per_page,
                'has_next': end < total,
                'has_prev': page > 1
            }


# Global search backend instance
search_backend = SearchBackend()
