#!/usr/bin/env python3
"""
Update Manager for Capcat.
Handles the --update flag logic for checking existing content and prompting users.
"""

import os
from datetime import datetime
from pathlib import Path
from typing import List, Tuple

from capcat.core.logging_config import get_logger
from capcat.core.utils import sanitize_filename


class UpdateManager:
    """
    Manages update operations for Capcat.

    Handles:
    - Date checking for existing content
    - User prompts for missing articles/bundles
    - Update logic without deleting old content
    """

    def __init__(self):
        self.logger = get_logger(__name__)
        self.today = datetime.now().strftime("%d-%m-%Y")

        # Base paths
        application_dir = os.path.dirname(
            os.path.dirname(os.path.abspath(__file__))
        )
        self.project_root = os.path.dirname(application_dir)
        self.news_dir = os.path.join(self.project_root, "News")
        self.capcats_dir = os.path.join(self.project_root, "Capcats")

    def check_and_handle_update(
        self,
        command_type: str,
        sources: List[str] = None,
        bundle_name: str = None,
        url: str = None,
    ) -> bool:
        """
        Main update handler. Returns True if update should proceed, False if cancelled.

        Args:
            command_type: 'single', 'fetch', or 'bundle'
            sources: List of source names for fetch command
            bundle_name: Bundle name for bundle command
            url: URL for single command

        Returns:
            bool: True if update should proceed, False if cancelled
        """
        try:
            self.logger.info(
                f"UPDATE MODE: Checking {command_type} for updates..."
            )
            self.logger.debug(
                f"Update parameters: sources={sources}, bundle={bundle_name}, url={url}"
            )

            if command_type == "single":
                return self._handle_single_update(url)
            elif command_type == "fetch":
                return self._handle_fetch_update(sources)
            elif command_type == "bundle":
                return self._handle_bundle_update(bundle_name)
            else:
                self.logger.error(f"Unknown command type: {command_type}")
                return False

        except Exception as e:
            self.logger.error(f"Error in update handler: {e}")
            return False

    def _handle_single_update(self, url: str) -> bool:
        """Handle update for single article command."""
        if not url:
            self.logger.error("No URL provided for single article update")
            return False

        # Determine expected article path
        try:
            from capcat.core.source_system.base_source import detect_source_from_url

            source = detect_source_from_url(url)
        except ImportError:
            # Fallback to legacy detection
            source = None

        if source:
            # Known source - check in Capcats
            source_folder_name = f"{source.title()}_{self.today}"
            expected_path = os.path.join(
                self.capcats_dir, source_folder_name
            )
        else:
            # Unknown source - check generic pattern
            from urllib.parse import urlparse

            parsed_url = urlparse(url)
            url_path = parsed_url.path.rstrip("/")
            url_title = (
                url_path.split("/")[-1] if url_path else parsed_url.netloc
            )
            safe_title = sanitize_filename(url_title, max_length=50)
            cc_folder_name = f"cc_{self.today}-{safe_title}"
            expected_path = os.path.join(self.capcats_dir, cc_folder_name)

        # Check if article exists
        if os.path.exists(expected_path):
            self.logger.info(f"Article already exists at: {expected_path}")
            return self._prompt_update_existing_article(expected_path, url)
        else:
            # Article doesn't exist - prompt to download
            return self._prompt_download_missing_article(url)

    def _handle_fetch_update(self, sources: List[str]) -> bool:
        """Handle update for fetch command."""
        if not sources:
            self.logger.error("No sources provided for fetch update")
            return False

        today_news_dir = os.path.join(self.news_dir, f"news_{self.today}")

        # Check if today's news directory exists
        if not os.path.exists(today_news_dir):
            return self._prompt_start_todays_batch("fetch", sources)

        # Check which sources exist for today
        existing_sources = []
        missing_sources = []

        for source in sources:
            # Get the actual folder name used by the system
            source_folder_name = self._get_source_folder_name(source)
            source_path = os.path.join(today_news_dir, source_folder_name)

            if os.path.exists(source_path):
                existing_sources.append((source, source_path))
            else:
                missing_sources.append(source)

        # Handle the update logic
        if existing_sources and missing_sources:
            # Some exist, some don't
            return self._prompt_mixed_source_update(
                existing_sources, missing_sources
            )
        elif existing_sources and not missing_sources:
            # All exist
            return self._prompt_update_all_existing_sources(existing_sources)
        else:
            # None exist
            return self._prompt_download_missing_sources(sources)

    def _handle_bundle_update(self, bundle_name: str) -> bool:
        """Handle update for bundle command."""
        if not bundle_name:
            self.logger.error("No bundle name provided for bundle update")
            return False

        today_news_dir = os.path.join(self.news_dir, f"news_{self.today}")

        # Check if today's news directory exists
        if not os.path.exists(today_news_dir):
            return self._prompt_start_todays_batch("bundle", [bundle_name])

        # Get bundle sources
        from capcat.core.source_system.bundle_service import get_available_bundles
        bundles = get_available_bundles(project_root=Path(self.project_root))

        if bundle_name not in bundles:
            self.logger.error(f"Unknown bundle: {bundle_name}")
            return False

        bundle_sources = bundles[bundle_name]

        # Check which sources exist for today
        existing_sources = []
        missing_sources = []

        for source in bundle_sources:
            # Get the actual folder name used by the system
            source_folder_name = self._get_source_folder_name(source)
            source_path = os.path.join(today_news_dir, source_folder_name)

            if os.path.exists(source_path):
                existing_sources.append((source, source_path))
            else:
                missing_sources.append(source)

        # Handle the update logic
        if existing_sources and missing_sources:
            # Some exist, some don't
            return self._prompt_mixed_bundle_update(
                bundle_name, existing_sources, missing_sources
            )
        elif existing_sources and not missing_sources:
            # All exist
            return self._prompt_update_existing_bundle(
                bundle_name, existing_sources
            )
        else:
            # None exist
            return self._prompt_download_missing_bundle(
                bundle_name, bundle_sources
            )

    def _prompt_update_existing_article(
        self, article_path: str, url: str
    ) -> bool:
        """Prompt user for updating an existing article."""
        try:
            response = (
                input(
                    f"\nArticle already exists at: {os.path.basename(article_path)}\n"
                    f"Do you want to update it with latest content? (Yes/No): "
                )
                .strip()
                .lower()
            )

            if response in ["y", "yes"]:
                self.logger.info("User chose to update existing article")
                return True
            else:
                self.logger.info("User chose not to update article")
                return False

        except KeyboardInterrupt:
            self.logger.info("\nOperation cancelled by user")
            return False

    def _prompt_download_missing_article(self, url: str) -> bool:
        """Prompt user to download a missing article."""
        try:
            response = (
                input(
                    "\nYou don't have this article downloaded yet.\n"
                    "Do you want to download it? (Yes/No): "
                )
                .strip()
                .lower()
            )

            if response in ["y", "yes"]:
                self.logger.info("User chose to download missing article")
                return True
            else:
                self.logger.info("User chose not to download article")
                return False

        except KeyboardInterrupt:
            self.logger.info("\nOperation cancelled by user")
            return False

    def _prompt_start_todays_batch(
        self, command_type: str, sources_or_bundles: List[str]
    ) -> bool:
        """Prompt user to start today's batch since none exists."""
        try:
            if command_type == "bundle":
                bundle_name = sources_or_bundles[0]
                response = (
                    input(
                        f"\nYou don't have the '{bundle_name}' bundle executed today.\n"
                        f"Would you like to start it? (Yes/No): "
                    )
                    .strip()
                    .lower()
                )
            else:
                sources_str = ", ".join(sources_or_bundles)
                response = (
                    input(
                        f"\nYou don't have any content from sources '{sources_str}' for today.\n"
                        f"Would you like to start fetching? (Yes/No): "
                    )
                    .strip()
                    .lower()
                )

            if response in ["y", "yes"]:
                self.logger.info("User chose to start today's batch")
                return True
            else:
                self.logger.info("User chose not to start batch")
                return False

        except KeyboardInterrupt:
            self.logger.info("\nOperation cancelled by user")
            return False

    def _prompt_mixed_source_update(
        self,
        existing_sources: List[Tuple[str, str]],
        missing_sources: List[str],
    ) -> bool:
        """Prompt when some sources exist and some don't."""
        try:
            existing_names = [source for source, _ in existing_sources]
            existing_str = ", ".join(existing_names)
            missing_str = ", ".join(missing_sources)

            print("\nUpdate Status:")
            print(f"✅ Existing: {existing_str}")
            print(f"❌ Missing: {missing_str}")

            response = (
                input(
                    "\nDo you want to update existing sources and download missing ones? (Yes/No): "
                )
                .strip()
                .lower()
            )

            if response in ["y", "yes"]:
                self.logger.info(
                    "User chose to update existing and download missing sources"
                )
                return True
            else:
                self.logger.info("User chose not to proceed with mixed update")
                return False

        except KeyboardInterrupt:
            self.logger.info("\nOperation cancelled by user")
            return False

    def _prompt_update_all_existing_sources(
        self, existing_sources: List[Tuple[str, str]]
    ) -> bool:
        """Prompt when all sources exist."""
        existing_names = [source for source, _ in existing_sources]
        existing_str = ", ".join(existing_names)

        self.logger.info(f"UPDATE MODE: Getting the latest articles from the targeted source ({existing_str})")
        return True

    def _prompt_download_missing_sources(self, sources: List[str]) -> bool:
        """Auto-download missing sources in update mode."""
        sources_str = ", ".join(sources)
        self.logger.info(f"UPDATE MODE: Getting the latest articles from the targeted source ({sources_str})")
        return True

    def _prompt_mixed_bundle_update(
        self,
        bundle_name: str,
        existing_sources: List[Tuple[str, str]],
        missing_sources: List[str],
    ) -> bool:
        """Prompt when some bundle sources exist and some don't."""
        try:
            existing_names = [source for source, _ in existing_sources]
            existing_str = ", ".join(existing_names)
            missing_str = ", ".join(missing_sources)

            print(f"\nBundle '{bundle_name}' Status:")
            print(f"✅ Existing: {existing_str}")
            print(f"❌ Missing: {missing_str}")

            response = (
                input(
                    "\nDo you want to update existing and download missing sources? (Yes/No): "
                )
                .strip()
                .lower()
            )

            if response in ["y", "yes"]:
                self.logger.info(
                    f"User chose to update bundle '{bundle_name}' (mixed)"
                )
                return True
            else:
                self.logger.info(
                    "User chose not to proceed with bundle update"
                )
                return False

        except KeyboardInterrupt:
            self.logger.info("\nOperation cancelled by user")
            return False

    def _prompt_update_existing_bundle(
        self, bundle_name: str, existing_sources: List[Tuple[str, str]]
    ) -> bool:
        """Prompt when entire bundle exists."""
        existing_names = [source for source, _ in existing_sources]
        existing_str = ", ".join(existing_names)

        self.logger.info(f"UPDATE MODE: Getting the latest articles from the targeted source ({existing_str})")
        return True

    def _prompt_download_missing_bundle(
        self, bundle_name: str, bundle_sources: List[str]
    ) -> bool:
        """Prompt when no bundle sources exist."""
        sources_str = ", ".join(bundle_sources)
        self.logger.info(f"UPDATE MODE: Getting the latest articles from the targeted source ({sources_str})")
        return True

    def _get_source_folder_name(self, source: str) -> str:
        """Get the actual folder name used by the system for a source."""
        from capcat.core.utils import get_source_folder_name
        base_name = get_source_folder_name(source)
        return f"{base_name} {self.today}"


def get_update_manager() -> UpdateManager:
    """Get a singleton instance of UpdateManager."""
    if not hasattr(get_update_manager, "_instance"):
        get_update_manager._instance = UpdateManager()
    return get_update_manager._instance
