from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

from audiorag.core.config import AudioRAGConfig
from audiorag.core.logging_config import get_logger
from audiorag.core.protocols import (
    AudioSourceProvider,
    EmbeddingProvider,
    GenerationProvider,
    RerankerProvider,
    STTProvider,
    VectorStoreProvider,
)
from audiorag.core.retry_config import RetryConfig

if TYPE_CHECKING:
    from audiorag.core.models import AudioFile, SourceMetadata


class AudioSourceRouter:
    """Router that delegates to the appropriate audio source provider based on URL.

    Auto-detects:
    - file:// URLs or local paths → LocalSource
    - youtube.com, youtu.be → YouTubeSource
    - Direct HTTP URLs → URLSource
    """

    def __init__(
        self,
        youtube_provider: AudioSourceProvider,
        local_provider: AudioSourceProvider,
        url_provider: AudioSourceProvider,
    ) -> None:
        self._youtube = youtube_provider
        self._local = local_provider
        self._url = url_provider
        self._logger = get_logger("audiorag.source_router")

    @staticmethod
    def _is_youtube_url(url: str) -> bool:
        """Check if URL is a YouTube URL using proper domain matching."""
        try:
            from urllib.parse import urlparse

            parsed = urlparse(url)
            netloc = parsed.netloc.lower()

            # Match exact youtu.be or any subdomain (www, etc.)
            if netloc == "youtu.be" or netloc.endswith(".youtu.be"):
                return True

            # For youtube.com, ensure it's either exact match or a known subdomain
            return netloc == "youtube.com" or netloc.endswith(".youtube.com")
        except Exception:
            return "youtube.com" in url or "youtu.be" in url

    def _get_provider(self, url: str) -> tuple[AudioSourceProvider, str]:
        """Determine which provider to use based on URL."""
        # Check for file:// protocol or local path
        # Local paths: absolute (/home/user/audio.mp3), relative (./audio.mp3),
        # or with file:// prefix
        if url.startswith("file://"):
            return self._local, "local"

        # Check if it looks like a local path (starts with /, ./, ../)
        # This catches absolute paths like /home/user/audio.mp3 and relative paths
        if url.startswith("/") or url.startswith("./") or url.startswith("../"):
            return self._local, "local"

        # Check for local path that exists
        if Path(url).exists():
            return self._local, "local"

        # Check for YouTube URLs
        if self._is_youtube_url(url):
            return self._youtube, "youtube"

        # Default to URL source for direct HTTP/HTTPS URLs
        return self._url, "url"

    async def get_metadata(self, url: str) -> SourceMetadata | None:
        """Get metadata using the appropriate provider."""
        provider, _ = self._get_provider(url)
        return await provider.get_metadata(url)

    async def download(
        self,
        url: str,
        output_dir: Path,
        audio_format: str = "mp3",
        metadata: SourceMetadata | None = None,
    ) -> AudioFile:
        """Download audio using the appropriate provider."""
        provider, _ = self._get_provider(url)
        return await provider.download(url, output_dir, audio_format, metadata)


def create_audio_source_provider(config: AudioRAGConfig) -> AudioSourceProvider:
    """Create an audio source provider based on config.

    Returns an AudioSourceRouter that auto-detects URL protocol:
    - file:// URLs or local paths → LocalSource
    - youtube.com, youtu.be → YouTubeSource
    - Direct HTTP URLs → URLSource
    """
    from audiorag.pipeline import _build_ydl_opts
    from audiorag.source.local import LocalSource
    from audiorag.source.url import URLSource
    from audiorag.source.youtube import YouTubeSource

    provider_name = config.audio_source_provider.lower()

    # Create providers
    local_provider = LocalSource()
    url_provider = URLSource()

    if provider_name == "local":
        # Return just local provider if explicitly configured
        return local_provider
    if provider_name == "url":
        # Return just url provider if explicitly configured
        return url_provider

    # Default: YouTube provider with full config
    archive_path = (
        Path(config.youtube_download_archive) if config.youtube_download_archive else None
    )
    ydl_opts = _build_ydl_opts(config)
    youtube_provider = YouTubeSource(
        download_archive=archive_path,
        ydl_opts=ydl_opts,
    )

    # Return router that auto-detects based on URL
    return AudioSourceRouter(
        youtube_provider=youtube_provider,
        local_provider=local_provider,
        url_provider=url_provider,
    )


def create_stt_provider(config: AudioRAGConfig, retry_config: RetryConfig) -> STTProvider:
    provider_name = config.stt_provider.lower()

    if provider_name == "groq":
        from audiorag.transcribe.groq import GroqTranscriber

        return GroqTranscriber(
            api_key=config.groq_api_key or None,
            model=config.get_stt_model(),
            retry_config=retry_config,
        )
    if provider_name == "deepgram":
        from audiorag.transcribe.deepgram import DeepgramTranscriber

        return DeepgramTranscriber(
            api_key=config.deepgram_api_key or None,
            model=config.get_stt_model(),
            retry_config=retry_config,
        )
    if provider_name == "assemblyai":
        from audiorag.transcribe.assemblyai import AssemblyAITranscriber

        return AssemblyAITranscriber(
            api_key=config.assemblyai_api_key or None,
            model=config.get_stt_model(),
            retry_config=retry_config,
        )
    from audiorag.transcribe.openai import OpenAITranscriber

    return OpenAITranscriber(
        api_key=config.openai_api_key or None,
        model=config.stt_model,
        retry_config=retry_config,
    )


def create_embedding_provider(
    config: AudioRAGConfig, retry_config: RetryConfig
) -> EmbeddingProvider:
    provider_name = config.embedding_provider.lower()

    if provider_name == "voyage":
        from audiorag.embed.voyage import VoyageEmbeddingProvider

        return VoyageEmbeddingProvider(
            api_key=config.voyage_api_key or None,
            model=config.get_embedding_model(),
            retry_config=retry_config,
        )
    if provider_name == "cohere":
        from audiorag.embed.cohere import CohereEmbeddingProvider

        return CohereEmbeddingProvider(
            api_key=config.cohere_api_key or None,
            model=config.get_embedding_model(),
            retry_config=retry_config,
        )
    from audiorag.embed.openai import OpenAIEmbeddingProvider

    return OpenAIEmbeddingProvider(
        api_key=config.openai_api_key or None,
        model=config.embedding_model,
        retry_config=retry_config,
    )


def create_vector_store_provider(
    config: AudioRAGConfig, retry_config: RetryConfig
) -> VectorStoreProvider:
    provider_name = config.vector_store_provider.lower()

    if provider_name == "supabase":
        try:
            from audiorag.store.supabase import SupabasePgVectorStore
        except ImportError as e:
            raise ImportError(
                "supabase vector store requires 'supabase' package. "
                "Install with: pip install audiorag[supabase]"
            ) from e
        return SupabasePgVectorStore(
            connection_string=config.supabase_connection_string or "",
            collection_name=config.supabase_collection_name or "audiorag",
            dimension=config.supabase_vector_dimension,
            retry_config=retry_config,
        )
    if provider_name == "pinecone":
        try:
            from audiorag.store.pinecone import PineconeVectorStore
        except ImportError as e:
            raise ImportError(
                "pinecone vector store requires 'pinecone-client' package. "
                "Install with: pip install audiorag[pinecone]"
            ) from e
        return PineconeVectorStore(
            api_key=config.pinecone_api_key or "",
            index_name=config.pinecone_index_name or "audiorag",
            namespace=config.pinecone_namespace or "default",
            retry_config=retry_config,
        )
    if provider_name == "weaviate":
        try:
            from audiorag.store.weaviate import WeaviateVectorStore
        except ImportError as e:
            raise ImportError(
                "weaviate vector store requires 'weaviate-client' package. "
                "Install with: pip install audiorag[weaviate]"
            ) from e
        return WeaviateVectorStore(
            url=config.weaviate_url or None,
            api_key=config.weaviate_api_key or None,
            collection_name=config.weaviate_collection_name or "AudioRAG",
            retry_config=retry_config,
        )
    try:
        from audiorag.store.chromadb import ChromaDBVectorStore
    except ImportError as e:
        raise ImportError(
            "chromadb vector store requires 'chromadb' package. "
            "Install with: pip install audiorag[chromadb]"
        ) from e
    return ChromaDBVectorStore(
        persist_directory=config.chromadb_persist_directory or "./chroma_db",
        collection_name=config.chromadb_collection_name or "audiorag",
        retry_config=retry_config,
    )


def create_generation_provider(
    config: AudioRAGConfig, retry_config: RetryConfig
) -> GenerationProvider:
    provider_name = config.generation_provider.lower()

    if provider_name == "anthropic":
        from audiorag.generate.anthropic import AnthropicGenerator

        return AnthropicGenerator(
            api_key=config.anthropic_api_key or None,
            model=config.generation_model or "claude-3-7-sonnet-20250219",
            retry_config=retry_config,
        )
    if provider_name == "gemini":
        from audiorag.generate.gemini import GeminiGenerator

        return GeminiGenerator(
            api_key=config.google_api_key or None,
            model=config.generation_model or "gemini-2.0-flash-001",
            retry_config=retry_config,
        )
    from audiorag.generate.openai import OpenAIGenerator

    return OpenAIGenerator(
        api_key=config.openai_api_key or None,
        model=config.generation_model or "gpt-4o-mini",
        retry_config=retry_config,
    )


def create_reranker_provider(config: AudioRAGConfig, retry_config: RetryConfig) -> RerankerProvider:
    provider_name = config.reranker_provider.lower()

    if provider_name == "passthrough" or not config.cohere_api_key:
        from audiorag.rerank.passthrough import PassthroughReranker

        return PassthroughReranker()

    from audiorag.rerank.cohere import CohereReranker

    return CohereReranker(
        api_key=config.cohere_api_key or None,
        model=config.reranker_model or "rerank-v3.5",
        retry_config=retry_config,
    )
