"""
Scheduler Router

Handles all scheduler and service management operations:
- Scheduler status and task management
- Current activity monitoring
- Task pause/resume/skip operations
- Service start/stop/restart
- Cache builder service management
- Dependency updates
"""

import json
import os
import re
import signal
import sqlite3
import subprocess
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict

from fastapi import APIRouter, Depends, HTTPException, Request
from slowapi import Limiter
from slowapi.util import get_remote_address

from ..core.dependencies import get_current_user, require_admin, get_app_state
from ..core.config import settings
from ..core.exceptions import (
    handle_exceptions,
    RecordNotFoundError,
    ServiceError
)
from ..core.responses import now_iso8601
from modules.universal_logger import get_logger

logger = get_logger('API')

router = APIRouter(prefix="/api/scheduler", tags=["Scheduler"])
limiter = Limiter(key_func=get_remote_address)

# Service names
SCHEDULER_SERVICE = 'media-downloader.service'
CACHE_BUILDER_SERVICE = 'media-cache-builder.service'

# Valid platform names for subprocess operations (defense in depth)
VALID_PLATFORMS = frozenset(['fastdl', 'imginn', 'imginn_api', 'toolzu', 'snapchat', 'tiktok', 'forums', 'coppermine', 'instagram', 'youtube'])

# Display name mapping for scheduler task_id prefixes
PLATFORM_DISPLAY_NAMES = {
    'fastdl': 'FastDL',
    'imginn': 'ImgInn',
    'imginn_api': 'ImgInn API',
    'toolzu': 'Toolzu',
    'snapchat': 'Snapchat',
    'tiktok': 'TikTok',
    'forums': 'Forums',
    'forum': 'Forum',
    'monitor': 'Forum Monitor',
    'instagram': 'Instagram',
    'youtube': 'YouTube',
    'youtube_channel_monitor': 'YouTube Channels',
    'youtube_monitor': 'YouTube Monitor',
    'coppermine': 'Coppermine',
    'paid_content': 'Paid Content',
    'appearances': 'Appearances',
    'easynews_monitor': 'Easynews Monitor',
    'press_monitor': 'Press Monitor',
}


# ============================================================================
# SCHEDULER STATUS ENDPOINTS
# ============================================================================

@router.get("/status")
@limiter.limit("100/minute")
@handle_exceptions
async def get_scheduler_status(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Get detailed scheduler status including all tasks."""
    app_state = get_app_state()

    # Get enabled forums from config to filter scheduler tasks
    enabled_forums = set()
    forums_config = app_state.settings.get('forums')
    if forums_config and isinstance(forums_config, dict):
        for forum_cfg in forums_config.get('configs', []):
            if forum_cfg.get('enabled', False):
                enabled_forums.add(forum_cfg.get('name'))

    with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn:
        cursor = sched_conn.cursor()

        # Get all tasks
        cursor.execute("""
            SELECT task_id, last_run, next_run, run_count, status, last_download_count
            FROM scheduler_state
            ORDER BY next_run ASC
        """)
        tasks_raw = cursor.fetchall()

        # Clean up stale forum/monitor entries
        stale_task_ids = []
        # Platforms that should always have :username suffix
        platforms_requiring_username = {'tiktok', 'instagram', 'imginn', 'imginn_api', 'toolzu', 'snapchat', 'fastdl'}

        for row in tasks_raw:
            task_id = row[0]
            if task_id.startswith('forum:') or task_id.startswith('monitor:'):
                forum_name = task_id.split(':', 1)[1]
                if forum_name not in enabled_forums:
                    stale_task_ids.append(task_id)
            # Clean up legacy platform entries without :username suffix
            elif task_id in platforms_requiring_username:
                stale_task_ids.append(task_id)

        # Delete stale entries
        if stale_task_ids:
            for stale_id in stale_task_ids:
                cursor.execute("DELETE FROM scheduler_state WHERE task_id = ?", (stale_id,))
            sched_conn.commit()

        tasks = []
        for row in tasks_raw:
            task_id = row[0]
            # Skip stale and maintenance tasks
            if task_id in stale_task_ids:
                continue
            if task_id.startswith('maintenance:'):
                continue

            tasks.append({
                "task_id": task_id,
                "last_run": row[1],
                "next_run": row[2],
                "run_count": row[3],
                "status": row[4],
                "last_download_count": row[5]
            })

        # Count active tasks
        active_count = sum(1 for t in tasks if t['status'] == 'active')

        # Get next run time
        next_run = None
        for task in sorted(tasks, key=lambda t: t['next_run'] or ''):
            if task['status'] == 'active' and task['next_run']:
                next_run = task['next_run']
                break

    return {
        "running": active_count > 0,
        "tasks": tasks,
        "total_tasks": len(tasks),
        "active_tasks": active_count,
        "next_run": next_run
    }


@router.get("/current-activity")
@limiter.limit("100/minute")
@handle_exceptions
async def get_current_activity(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Get current scheduler activity for real-time status."""
    app_state = get_app_state()

    # Check if scheduler service is running
    result = subprocess.run(
        ['systemctl', 'is-active', SCHEDULER_SERVICE],
        capture_output=True,
        text=True
    )
    scheduler_running = result.stdout.strip() == 'active'

    if not scheduler_running:
        return {
            "active": False,
            "scheduler_running": False,
            "task_id": None,
            "platform": None,
            "account": None,
            "start_time": None,
            "status": None
        }

    # Get current activity from database
    from modules.activity_status import get_activity_manager
    activity_manager = get_activity_manager(app_state.db)
    activity = activity_manager.get_current_activity()
    activity["scheduler_running"] = True
    return activity


@router.get("/background-tasks")
@limiter.limit("100/minute")
@handle_exceptions
async def get_background_tasks(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Get all active background tasks (YouTube monitor, etc.) for real-time status."""
    app_state = get_app_state()

    from modules.activity_status import get_activity_manager
    activity_manager = get_activity_manager(app_state.db)
    tasks = activity_manager.get_active_background_tasks()
    return {"tasks": tasks}


@router.get("/background-tasks/{task_id}")
@limiter.limit("100/minute")
@handle_exceptions
async def get_background_task(
    task_id: str,
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Get a specific background task status."""
    app_state = get_app_state()

    from modules.activity_status import get_activity_manager
    activity_manager = get_activity_manager(app_state.db)
    task = activity_manager.get_background_task(task_id)

    if not task:
        return {"active": False, "task_id": task_id}
    return task


@router.post("/current-activity/stop")
@limiter.limit("20/minute")
@handle_exceptions
async def stop_current_activity(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Stop the currently running download task."""
    app_state = get_app_state()
    activity_file = settings.PROJECT_ROOT / 'database' / 'current_activity.json'

    if not activity_file.exists():
        raise RecordNotFoundError("No active task running")

    with open(activity_file, 'r') as f:
        activity_data = json.load(f)

    if not activity_data.get('active'):
        raise RecordNotFoundError("No active task running")

    task_id = activity_data.get('task_id')
    platform = activity_data.get('platform')

    # Security: Validate platform before using in subprocess (defense in depth)
    if platform and platform not in VALID_PLATFORMS:
        logger.warning(f"Invalid platform in activity file: {platform}", module="Security")
        platform = None

    # Find and kill the process
    if platform:
        result = subprocess.run(
            ['pgrep', '-f', f'media-downloader\\.py.*--platform.*{re.escape(platform)}'],
            capture_output=True,
            text=True
        )
    else:
        # Fallback: find any media-downloader process
        result = subprocess.run(
            ['pgrep', '-f', 'media-downloader\\.py'],
            capture_output=True,
            text=True
        )

    if result.stdout.strip():
        pids = [p.strip() for p in result.stdout.strip().split('\n') if p.strip()]
        for pid in pids:
            try:
                os.kill(int(pid), signal.SIGTERM)
                logger.info(f"Stopped process {pid} for platform {platform}")
            except (ProcessLookupError, ValueError):
                pass

    # Clear the current activity
    inactive_state = {
        "active": False,
        "task_id": None,
        "platform": None,
        "account": None,
        "start_time": None,
        "status": "stopped"
    }

    with open(activity_file, 'w') as f:
        json.dump(inactive_state, f)

    # Broadcast stop event
    try:
        if hasattr(app_state, 'websocket_manager') and app_state.websocket_manager:
            await app_state.websocket_manager.broadcast({
                "type": "download_stopped",
                "task_id": task_id,
                "platform": platform,
                "timestamp": now_iso8601()
            })
    except Exception:
        pass

    return {
        "success": True,
        "message": f"Stopped {platform} download",
        "task_id": task_id
    }


# ============================================================================
# TASK MANAGEMENT ENDPOINTS
# ============================================================================

@router.post("/tasks/{task_id}/pause")
@limiter.limit("20/minute")
@handle_exceptions
async def pause_scheduler_task(
    request: Request,
    task_id: str,
    current_user: Dict = Depends(get_current_user)
):
    """Pause a specific scheduler task."""
    app_state = get_app_state()

    with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn:
        cursor = sched_conn.cursor()

        cursor.execute("""
            UPDATE scheduler_state
            SET status = 'paused'
            WHERE task_id = ?
        """, (task_id,))

        sched_conn.commit()
        row_count = cursor.rowcount

    if row_count == 0:
        raise RecordNotFoundError("Task not found", {"task_id": task_id})

    # Broadcast event
    try:
        if hasattr(app_state, 'websocket_manager') and app_state.websocket_manager:
            await app_state.websocket_manager.broadcast({
                "type": "scheduler_task_paused",
                "task_id": task_id,
                "timestamp": now_iso8601()
            })
    except Exception:
        pass

    return {"success": True, "task_id": task_id, "status": "paused"}


@router.post("/tasks/{task_id}/resume")
@limiter.limit("20/minute")
@handle_exceptions
async def resume_scheduler_task(
    request: Request,
    task_id: str,
    current_user: Dict = Depends(get_current_user)
):
    """Resume a paused scheduler task."""
    app_state = get_app_state()

    with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn:
        cursor = sched_conn.cursor()

        cursor.execute("""
            UPDATE scheduler_state
            SET status = 'active'
            WHERE task_id = ?
        """, (task_id,))

        sched_conn.commit()
        row_count = cursor.rowcount

    if row_count == 0:
        raise RecordNotFoundError("Task not found", {"task_id": task_id})

    # Broadcast event
    try:
        if hasattr(app_state, 'websocket_manager') and app_state.websocket_manager:
            await app_state.websocket_manager.broadcast({
                "type": "scheduler_task_resumed",
                "task_id": task_id,
                "timestamp": now_iso8601()
            })
    except Exception:
        pass

    return {"success": True, "task_id": task_id, "status": "active"}


@router.post("/tasks/{task_id}/skip")
@limiter.limit("20/minute")
@handle_exceptions
async def skip_next_run(
    request: Request,
    task_id: str,
    current_user: Dict = Depends(get_current_user)
):
    """Skip the next scheduled run by advancing next_run time."""
    app_state = get_app_state()

    with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn:
        cursor = sched_conn.cursor()

        # Get current task info
        cursor.execute("""
            SELECT next_run, interval_hours
            FROM scheduler_state
            WHERE task_id = ?
        """, (task_id,))

        result = cursor.fetchone()
        if not result:
            raise RecordNotFoundError("Task not found", {"task_id": task_id})

        current_next_run, interval_hours = result

        # Calculate new next_run time
        current_time = datetime.fromisoformat(current_next_run)
        new_next_run = current_time + timedelta(hours=interval_hours)

        # Update the next_run time
        cursor.execute("""
            UPDATE scheduler_state
            SET next_run = ?
            WHERE task_id = ?
        """, (new_next_run.isoformat(), task_id))

        sched_conn.commit()

    # Broadcast event
    try:
        if hasattr(app_state, 'websocket_manager') and app_state.websocket_manager:
            await app_state.websocket_manager.broadcast({
                "type": "scheduler_run_skipped",
                "task_id": task_id,
                "new_next_run": new_next_run.isoformat(),
                "timestamp": now_iso8601()
            })
    except Exception:
        pass

    return {
        "success": True,
        "task_id": task_id,
        "skipped_run": current_next_run,
        "new_next_run": new_next_run.isoformat()
    }


@router.post("/tasks/{task_id}/reschedule")
@limiter.limit("20/minute")
@handle_exceptions
async def reschedule_task(
    request: Request,
    task_id: str,
    current_user: Dict = Depends(get_current_user)
):
    """Reschedule a task to a new next_run time."""
    body = await request.json()
    new_next_run = body.get('next_run')
    if not new_next_run:
        raise HTTPException(status_code=400, detail="next_run is required")

    try:
        parsed = datetime.fromisoformat(new_next_run)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid datetime format")

    with sqlite3.connect(str(settings.PROJECT_ROOT / 'database' / 'scheduler_state.db')) as sched_conn:
        cursor = sched_conn.cursor()
        cursor.execute(
            "UPDATE scheduler_state SET next_run = ? WHERE task_id = ?",
            (parsed.isoformat(), task_id)
        )
        sched_conn.commit()
        if cursor.rowcount == 0:
            raise RecordNotFoundError("Task not found", {"task_id": task_id})

    # Broadcast event
    try:
        app_state = get_app_state()
        if hasattr(app_state, 'websocket_manager') and app_state.websocket_manager:
            await app_state.websocket_manager.broadcast({
                "type": "scheduler_task_rescheduled",
                "task_id": task_id,
                "new_next_run": parsed.isoformat(),
                "timestamp": now_iso8601()
            })
    except Exception:
        pass

    return {"success": True, "task_id": task_id, "new_next_run": parsed.isoformat()}


# ============================================================================
# CONFIG RELOAD ENDPOINT
# ============================================================================

@router.post("/reload-config")
@limiter.limit("10/minute")
@handle_exceptions
async def reload_scheduler_config(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Reload scheduler config — picks up new/removed accounts and interval changes."""
    app_state = get_app_state()

    if not hasattr(app_state, 'scheduler') or app_state.scheduler is None:
        raise ServiceError("Scheduler is not running", {"service": SCHEDULER_SERVICE})

    result = app_state.scheduler.reload_scheduled_tasks()

    return {
        "success": True,
        "added": result['added'],
        "removed": result['removed'],
        "modified": result['modified'],
        "message": (
            f"Reload complete: {len(result['added'])} added, "
            f"{len(result['removed'])} removed, "
            f"{len(result['modified'])} modified"
        )
    }


# ============================================================================
# SERVICE MANAGEMENT ENDPOINTS
# ============================================================================

@router.get("/service/status")
@limiter.limit("100/minute")
@handle_exceptions
async def get_scheduler_service_status(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Check if scheduler service is running."""
    result = subprocess.run(
        ['systemctl', 'is-active', SCHEDULER_SERVICE],
        capture_output=True,
        text=True
    )
    is_running = result.stdout.strip() == 'active'

    return {
        "running": is_running,
        "status": result.stdout.strip()
    }


@router.post("/service/start")
@limiter.limit("20/minute")
@handle_exceptions
async def start_scheduler_service(
    request: Request,
    current_user: Dict = Depends(require_admin)  # Require admin for service operations
):
    """Start the scheduler service. Requires admin privileges."""
    result = subprocess.run(
        ['sudo', 'systemctl', 'start', SCHEDULER_SERVICE],
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        raise ServiceError(
            f"Failed to start service: {result.stderr}",
            {"service": SCHEDULER_SERVICE}
        )

    return {"success": True, "message": "Scheduler service started"}


@router.post("/service/stop")
@limiter.limit("20/minute")
@handle_exceptions
async def stop_scheduler_service(
    request: Request,
    current_user: Dict = Depends(require_admin)  # Require admin for service operations
):
    """Stop the scheduler service. Requires admin privileges."""
    result = subprocess.run(
        ['sudo', 'systemctl', 'stop', SCHEDULER_SERVICE],
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        raise ServiceError(
            f"Failed to stop service: {result.stderr}",
            {"service": SCHEDULER_SERVICE}
        )

    return {"success": True, "message": "Scheduler service stopped"}


@router.post("/service/restart")
@limiter.limit("20/minute")
@handle_exceptions
async def restart_scheduler_service(
    request: Request,
    current_user: Dict = Depends(require_admin)
):
    """Restart the scheduler service. Requires admin privileges."""
    result = subprocess.run(
        ['sudo', 'systemctl', 'restart', SCHEDULER_SERVICE],
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        raise ServiceError(
            f"Failed to restart service: {result.stderr}",
            {"service": SCHEDULER_SERVICE}
        )

    return {"success": True, "message": "Scheduler service restarted"}


# ============================================================================
# DEPENDENCY MANAGEMENT ENDPOINTS
# ============================================================================

@router.get("/dependencies/status")
@limiter.limit("100/minute")
@handle_exceptions
async def get_dependencies_status(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Get dependency update status."""
    from modules.dependency_updater import DependencyUpdater
    updater = DependencyUpdater(scheduler_mode=False)
    status = updater.get_update_status()
    return status


@router.post("/dependencies/check")
@limiter.limit("20/minute")
@handle_exceptions
async def check_dependencies(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Force check and update all dependencies."""
    app_state = get_app_state()

    from modules.dependency_updater import DependencyUpdater
    from modules.pushover_notifier import create_notifier_from_config

    # Get pushover config
    pushover = None
    config = app_state.settings.get_all()
    if config.get('pushover', {}).get('enabled'):
        pushover = create_notifier_from_config(config, unified_db=app_state.db)

    updater = DependencyUpdater(
        config=config.get('dependency_updater', {}),
        pushover_notifier=pushover,
        scheduler_mode=True
    )

    results = updater.force_update_check()
    return {
        "success": True,
        "results": results,
        "message": "Dependency check completed"
    }


# ============================================================================
# CACHE BUILDER SERVICE ENDPOINTS
# ============================================================================

@router.post("/cache-builder/trigger")
@limiter.limit("10/minute")
@handle_exceptions
async def trigger_cache_builder(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Manually trigger the thumbnail cache builder service."""
    result = subprocess.run(
        ['sudo', 'systemctl', 'start', CACHE_BUILDER_SERVICE],
        capture_output=True,
        text=True,
        timeout=5
    )

    if result.returncode == 0:
        return {"success": True, "message": "Cache builder started successfully"}
    else:
        raise ServiceError(
            f"Failed to start cache builder: {result.stderr}",
            {"service": CACHE_BUILDER_SERVICE}
        )


@router.get("/cache-builder/status")
@limiter.limit("30/minute")
@handle_exceptions
async def get_cache_builder_status(
    request: Request,
    current_user: Dict = Depends(get_current_user)
):
    """Get detailed cache builder service status."""
    # Get service status
    result = subprocess.run(
        ['systemctl', 'status', CACHE_BUILDER_SERVICE, '--no-pager'],
        capture_output=True,
        text=True
    )

    status_output = result.stdout

    # Parse status
    is_running = 'Active: active (running)' in status_output
    is_inactive = 'Active: inactive' in status_output
    last_run = None
    next_run = None

    # Try to get timer info
    timer_result = subprocess.run(
        ['systemctl', 'list-timers', '--no-pager', '--all'],
        capture_output=True,
        text=True
    )

    if CACHE_BUILDER_SERVICE.replace('.service', '') in timer_result.stdout:
        for line in timer_result.stdout.split('\n'):
            if CACHE_BUILDER_SERVICE.replace('.service', '') in line:
                parts = line.split()
                if len(parts) >= 2:
                    # Extract timing info if available
                    pass

    return {
        "running": is_running,
        "inactive": is_inactive,
        "status_output": status_output[:500],  # Truncate for brevity
        "last_run": last_run,
        "next_run": next_run
    }
