"""Sonarr webhook handler and integration logic using SQLModel"""
import hmac
import hashlib
import logging
from typing import Optional, Dict, List, Any
from datetime import datetime

from sqlmodel import Session, select
from app.database import engine
from app.models.sonarr import (
    SonarrWebhookPayload,
    SonarrEventType,
    SonarrMapping,
    SonarrMappingTable,
    SonarrConfig,
    SonarrConfigTable,
    SonarrDownloadRequest
)
from app.models import DownloadRequest
from app.downloaders import get_downloader, AnimeSamaDownloader, NekoSamaDownloader, AnimeUltimeDownloader, VostfreeDownloader

# Configure logging
logger = logging.getLogger(__name__)


class SonarrHandler:
    """Handles Sonarr webhooks and manages series mappings using SQL database"""

    def __init__(self, config_path: str = None, mappings_path: str = None):
        self.download_manager = None
        self._ensure_default_config()

    def set_download_manager(self, download_manager):
        self.download_manager = download_manager

    def _ensure_default_config(self):
        """Ensure a default config exists in the database"""
        with Session(engine) as session:
            statement = select(SonarrConfigTable)
            if not session.exec(statement).first():
                session.add(SonarrConfigTable())
                session.commit()

    def get_config(self) -> SonarrConfig:
        """Get current configuration"""
        with Session(engine) as session:
            statement = select(SonarrConfigTable)
            db_config = session.exec(statement).first()
            if db_config:
                return SonarrConfig(
                    webhook_enabled=db_config.webhook_enabled,
                    webhook_secret=db_config.webhook_secret,
                    auto_download_enabled=db_config.auto_download_enabled,
                    default_language=db_config.default_language,
                    default_quality=db_config.default_quality,
                    default_provider=db_config.default_provider,
                    verify_hmac=db_config.verify_hmac,
                    log_webhooks=db_config.log_webhooks
                )
            return SonarrConfig()

    def update_config(self, config: SonarrConfig) -> SonarrConfig:
        """Update configuration"""
        with Session(engine) as session:
            statement = select(SonarrConfigTable)
            db_config = session.exec(statement).first()
            
            if not db_config:
                db_config = SonarrConfigTable()
                
            db_config.webhook_enabled = config.webhook_enabled
            db_config.webhook_secret = config.webhook_secret
            db_config.auto_download_enabled = config.auto_download_enabled
            db_config.default_language = config.default_language
            db_config.default_quality = config.default_quality
            db_config.default_provider = config.default_provider
            db_config.verify_hmac = config.verify_hmac
            db_config.log_webhooks = config.log_webhooks
            
            session.add(db_config)
            session.commit()
            
        logger.info("Sonarr configuration updated in database")
        return config

    def _to_pydantic(self, db_mapping: SonarrMappingTable) -> SonarrMapping:
        return SonarrMapping(
            sonarr_series_id=db_mapping.sonarr_series_id,
            sonarr_title=db_mapping.sonarr_title,
            anime_provider=db_mapping.anime_provider,
            anime_url=db_mapping.anime_url,
            anime_title=db_mapping.anime_title,
            lang=db_mapping.lang,
            quality_preference=db_mapping.quality_preference,
            auto_download=db_mapping.auto_download,
            created_at=db_mapping.created_at,
            updated_at=db_mapping.updated_at
        )

    def get_mappings(self) -> List[SonarrMapping]:
        """Get all mappings"""
        with Session(engine) as session:
            statement = select(SonarrMappingTable)
            db_mappings = session.exec(statement).all()
            return [self._to_pydantic(m) for m in db_mappings]

    def get_mapping(self, sonarr_series_id: int) -> Optional[SonarrMapping]:
        """Get mapping for specific series"""
        with Session(engine) as session:
            statement = select(SonarrMappingTable).where(SonarrMappingTable.sonarr_series_id == sonarr_series_id)
            db_mapping = session.exec(statement).first()
            if db_mapping:
                return self._to_pydantic(db_mapping)
        return None

    def add_mapping(self, mapping: SonarrMapping) -> SonarrMapping:
        """Add or update a mapping"""
        with Session(engine) as session:
            statement = select(SonarrMappingTable).where(SonarrMappingTable.sonarr_series_id == mapping.sonarr_series_id)
            db_mapping = session.exec(statement).first()
            
            if db_mapping:
                # Update existing
                db_mapping.sonarr_title = mapping.sonarr_title
                db_mapping.anime_provider = mapping.anime_provider
                db_mapping.anime_url = mapping.anime_url
                db_mapping.anime_title = mapping.anime_title
                db_mapping.lang = mapping.lang
                db_mapping.quality_preference = mapping.quality_preference
                db_mapping.auto_download = mapping.auto_download
                db_mapping.updated_at = datetime.now()
                logger.info(f"Updated mapping for series {mapping.sonarr_title}")
            else:
                # Create new
                db_mapping = SonarrMappingTable(
                    user_id="default",
                    sonarr_series_id=mapping.sonarr_series_id,
                    sonarr_title=mapping.sonarr_title,
                    anime_provider=mapping.anime_provider,
                    anime_url=mapping.anime_url,
                    anime_title=mapping.anime_title,
                    lang=mapping.lang,
                    quality_preference=mapping.quality_preference,
                    auto_download=mapping.auto_download,
                    created_at=datetime.now(),
                    updated_at=datetime.now()
                )
                logger.info(f"Added mapping for series {mapping.sonarr_title}")
                
            session.add(db_mapping)
            session.commit()
            session.refresh(db_mapping)
            return self._to_pydantic(db_mapping)

    def delete_mapping(self, sonarr_series_id: int) -> bool:
        """Delete a mapping"""
        with Session(engine) as session:
            statement = select(SonarrMappingTable).where(SonarrMappingTable.sonarr_series_id == sonarr_series_id)
            db_mapping = session.exec(statement).first()
            if db_mapping:
                session.delete(db_mapping)
                session.commit()
                logger.info(f"Deleted mapping for series ID {sonarr_series_id}")
                return True
        return False

    def verify_hmac(self, payload: bytes, signature: str) -> bool:
        """Verify HMAC SHA256 signature"""
        config = self.get_config()
        if not config.verify_hmac or not config.webhook_secret:
            return True

        try:
            # Sonarr sends signature as 'sha256=<hex>'
            if signature.startswith('sha256='):
                signature = signature[7:]

            computed_hmac = hmac.new(
                config.webhook_secret.encode(),
                payload,
                hashlib.sha256
            ).hexdigest()

            return hmac.compare_digest(computed_hmac, signature)
        except Exception as e:
            logger.error(f"HMAC verification failed: {e}")
            return False

    async def search_anime_by_title(self, title: str, provider: str = "anime-sama", lang: str = "vostfr") -> List[Dict]:
        """Search for anime by title using specified provider"""
        try:
            downloader = self._get_provider_downloader(provider)
            if not downloader:
                logger.error(f"Provider {provider} not found")
                return []

            results = await downloader.search_anime(title, lang)
            logger.info(f"Found {len(results)} results for '{title}' on {provider}")
            return results
        except Exception as e:
            logger.error(f"Error searching anime: {e}")
            return []

    def _get_provider_downloader(self, provider: str):
        """Get downloader instance for provider"""
        providers = {
            "anime-sama": AnimeSamaDownloader(),
            "neko-sama": NekoSamaDownloader(),
            "anime-ultime": AnimeUltimeDownloader(),
            "vostfree": VostfreeDownloader()
        }
        return providers.get(provider)

    async def get_episodes_for_anime(self, anime_url: str, provider: str = "anime-sama", lang: str = "vostfr") -> List[Dict]:
        """Get episodes list for anime"""
        try:
            downloader = self._get_provider_downloader(provider)
            if not downloader:
                logger.error(f"Provider {provider} not found")
                return []

            episodes = await downloader.get_episodes(anime_url, lang)
            logger.info(f"Found {len(episodes)} episodes for {anime_url}")
            return episodes
        except Exception as e:
            logger.error(f"Error getting episodes: {e}")
            return []

    async def process_webhook(self, payload: SonarrWebhookPayload) -> Dict[str, Any]:
        """Process Sonarr webhook payload"""
        config = self.get_config()
        if not config.webhook_enabled:
            return {"status": "ignored", "reason": "Webhook not enabled"}

        if config.log_webhooks:
            logger.info(f"Received Sonarr webhook: {payload.eventType.value}")

        # Handle different event types
        if payload.eventType == SonarrEventType.GRAB:
            return await self._handle_grab(payload, config)
        elif payload.eventType == SonarrEventType.DOWNLOAD:
            return await self._handle_download(payload)
        elif payload.eventType == SonarrEventType.RENAME:
            return await self._handle_rename(payload)
        elif payload.eventType == SonarrEventType.DELETE:
            return await self._handle_delete(payload)
        elif payload.eventType == SonarrEventType.TEST:
            return {"status": "ok", "message": "Test webhook received"}
        else:
            return {"status": "ignored", "reason": f"Unhandled event type: {payload.eventType}"}

    async def _handle_grab(self, payload: SonarrWebhookPayload, config: SonarrConfig) -> Dict:
        """Handle Grab event (when Sonarr downloads a release)"""
        if not config.auto_download_enabled:
            return {"status": "ignored", "reason": "Auto-download disabled"}

        if not payload.series or not payload.episodes:
            return {"status": "error", "reason": "Missing series or episodes"}

        # Check for mapping
        mapping = self.get_mapping(payload.series.tvdbId)
        if not mapping:
            logger.info(f"No mapping found for series {payload.series.title} (ID: {payload.series.tvdbId})")
            return {
                "status": "no_mapping",
                "series": payload.series.title,
                "series_id": payload.series.tvdbId,
                "reason": "No anime mapping configured"
            }

        # Trigger download for each episode
        downloads = []
        for episode in payload.episodes:
            try:
                success = await self._trigger_download(
                    mapping,
                    episode.seasonNumber,
                    episode.episodeNumber
                )
                
                downloads.append({
                    "season": episode.seasonNumber,
                    "episode": episode.episodeNumber,
                    "status": "started" if success else "failed"
                })
            except Exception as e:
                logger.error(f"Failed to trigger download for episode {episode.episodeNumber}: {e}")
                downloads.append({
                    "season": episode.seasonNumber,
                    "episode": episode.episodeNumber,
                    "status": "error",
                    "error": str(e)
                })

        return {
            "status": "processing",
            "mapping": mapping.anime_title,
            "downloads_queued": len(downloads),
            "downloads": downloads
        }

    async def _trigger_download(self, mapping: SonarrMapping, season_number: int, episode_number: int) -> bool:
        if not self.download_manager:
            logger.error("DownloadManager not set in SonarrHandler")
            return False

        try:
            downloader = get_downloader(mapping.anime_url)
            if not downloader:
                logger.error(f"No downloader for {mapping.anime_url}")
                return False

            episodes = await downloader.get_episodes(mapping.anime_url, mapping.lang)
            
            target_episode = None
            for ep in episodes:
                if ep.get('episode_number') == episode_number:
                    if ep.get('season') and ep['season'] != season_number:
                        continue
                    target_episode = ep
                    break
            
            if not target_episode:
                logger.warning(f"Episode {episode_number} not found for {mapping.anime_title}")
                return False

            video_url, _ = await downloader.get_download_link(target_episode['url'])
            
            player_handler = get_downloader(video_url)
            download_url, filename = await player_handler.get_download_link(video_url)
            
            request = DownloadRequest(url=download_url, filename=filename)
            task = self.download_manager.create_task(request)
            
            if task:
                await self.download_manager.start_download(task.id)
                logger.info(f"Sonarr: Started download for {mapping.anime_title} S{season_number}E{episode_number}")
                return True
            
            return False
        except Exception as e:
            logger.error(f"Error triggering Sonarr download: {e}")
            return False

    async def _handle_download(self, payload: SonarrWebhookPayload) -> Dict:
        """Handle Download event (when Sonarr completes download)"""
        # Similar to Grab but for post-download processing
        logger.info(f"Download completed for {payload.series.title if payload.series else 'Unknown'}")
        return {"status": "ok", "message": "Download event logged"}

    async def _handle_rename(self, payload: SonarrWebhookPayload) -> Dict:
        """Handle Rename event (when Sonarr renames files)"""
        logger.info(f"Rename event for {payload.series.title if payload.series else 'Unknown'}")
        return {"status": "ok", "message": "Rename event logged"}

    async def _handle_delete(self, payload: SonarrWebhookPayload) -> Dict:
        """Handle Delete event"""
        logger.info(f"Delete event for series ID: {payload.series.tvdbId if payload.series else 'Unknown'}")
        return {"status": "ok", "message": "Delete event logged"}

    async def suggest_mapping(self, sonarr_title: str, provider: str = "anime-sama", lang: str = "vostfr") -> List[Dict]:
        """Suggest possible anime mappings based on Sonarr series title"""
        try:
            # Search for anime with similar title
            results = await self.search_anime_by_title(sonarr_title, provider, lang)

            suggestions = []
            for result in results[:10]:  # Limit to top 10 results
                suggestions.append({
                    "title": result.get('title'),
                    "url": result.get('url'),
                    "cover_image": result.get('cover_image'),
                    "match_score": self._calculate_match_score(sonarr_title, result.get('title', ''))
                })

            # Sort by match score
            suggestions.sort(key=lambda x: x['match_score'], reverse=True)
            return suggestions
        except Exception as e:
            logger.error(f"Error suggesting mappings: {e}")
            return []

    def _calculate_match_score(self, sonarr_title: str, anime_title: str) -> float:
        """Calculate similarity score between titles (simple implementation)"""
        # Simple case-insensitive comparison
        sonarr_lower = sonarr_title.lower()
        anime_lower = anime_title.lower()

        if sonarr_lower == anime_lower:
            return 1.0
        elif sonarr_lower in anime_lower or anime_lower in sonarr_lower:
            return 0.8
        else:
            # Calculate word overlap
            sonarr_words = set(sonarr_lower.split())
            anime_words = set(anime_lower.split())

            if not sonarr_words or not anime_words:
                return 0.0

            intersection = sonarr_words & anime_words
            union = sonarr_words | anime_words

            return len(intersection) / len(union) if union else 0.0


# Global instance
_sonarr_handler: Optional[SonarrHandler] = None


def get_sonarr_handler() -> SonarrHandler:
    """Get or create Sonarr handler instance"""
    global _sonarr_handler
    if _sonarr_handler is None:
        _sonarr_handler = SonarrHandler()
    return _sonarr_handler
