"""Transcription API routes."""

import uuid
from pathlib import Path
from threading import Thread, Lock
from collections import deque
from typing import List
from fastapi import APIRouter, HTTPException
from fastapi.responses import PlainTextResponse

from ..models import (
    TranscribeRequest,
    TranscribeResponse,
    StatusResponse,
    ResultResponse,
    JobStatus,
    VideoPreviewRequest,
    VideoPreview,
    QueueRequest,
    QueueStatusResponse,
    HistoryResponse,
    HistoryItem,
    ExportFormat,
)
from ..config import get_api_key, get_output_directory, get_temp_dir
from ..services.downloader import download_audio, get_video_info
from ..services.transcriber import transcribe_audio, cleanup_audio, text_to_srt, text_to_vtt
from ..services.history import (
    add_to_history, get_history, get_history_item,
    delete_history_item, get_transcription_text, clear_history,
    update_history_item
)
from ..services.analyzer import analyze_transcription, translate_text, format_transcription
from ..services.topics import (
    add_topics_to_video, remove_topic_from_video, get_video_topics,
    get_all_topics, search_topics, get_videos_by_topic, get_related_videos,
    delete_video_topics
)

router = APIRouter()

# In-memory job storage
jobs = {}
jobs_lock = Lock()

# Cancelled jobs set
cancelled_jobs = set()
cancelled_lock = Lock()

# Queue for batch processing
job_queue = deque()
queue_lock = Lock()
queue_processing = False


def is_cancelled(job_id: str) -> bool:
    """Check if a job has been cancelled."""
    with cancelled_lock:
        return job_id in cancelled_jobs


def save_transcription(title: str, text: str) -> str:
    """Save transcription to output directory and return file path."""
    output_dir = get_output_directory()
    safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).strip()
    safe_title = safe_title[:100] if len(safe_title) > 100 else safe_title
    if not safe_title:
        safe_title = "transcription"
    file_path = output_dir / f"{safe_title}.txt"

    counter = 1
    while file_path.exists():
        file_path = output_dir / f"{safe_title}_{counter}.txt"
        counter += 1

    with open(file_path, 'w', encoding='utf-8') as f:
        f.write(f"Title: {title}\n")
        f.write("=" * 50 + "\n\n")
        f.write(text)

    return str(file_path)


def update_job(job_id: str, **kwargs):
    """Thread-safe job update."""
    with jobs_lock:
        if job_id in jobs:
            jobs[job_id].update(kwargs)


def process_job(job_id: str, url: str):
    """Background task to download and transcribe video."""
    file_path = None
    try:
        api_key = get_api_key()
        if not api_key:
            raise Exception("OpenAI API key not configured")

        # Check cancellation
        if is_cancelled(job_id):
            update_job(job_id, status=JobStatus.ERROR, message="Cancelled", stage="Cancelled")
            return

        # Fetching info
        update_job(job_id, status=JobStatus.FETCHING_INFO, progress=5, stage="Fetching video info...")

        # Download progress callback
        def download_progress(percent, message):
            if is_cancelled(job_id):
                raise Exception("Cancelled")
            total_progress = 5 + int(percent * 0.35)
            update_job(job_id, progress=total_progress, stage=message)

        # Check cancellation
        if is_cancelled(job_id):
            update_job(job_id, status=JobStatus.ERROR, message="Cancelled", stage="Cancelled")
            return

        # Download audio
        update_job(job_id, status=JobStatus.DOWNLOADING, progress=10, stage="Starting download...")
        temp_dir = get_temp_dir()
        file_path, title, video_info = download_audio(url, str(temp_dir), download_progress)
        update_job(job_id, title=title, video_info=video_info, progress=40, stage="Download complete")

        # Check cancellation
        if is_cancelled(job_id):
            if file_path:
                cleanup_audio(file_path)
            update_job(job_id, status=JobStatus.ERROR, message="Cancelled", stage="Cancelled")
            return

        # Converting
        update_job(job_id, status=JobStatus.CONVERTING, progress=42, stage="Converting audio...")

        # Transcribe progress callback
        def transcribe_progress(percent, message):
            if is_cancelled(job_id):
                raise Exception("Cancelled")
            total_progress = 45 + int(percent * 0.45)
            update_job(job_id, progress=total_progress, stage=message)

        # Check cancellation
        if is_cancelled(job_id):
            if file_path:
                cleanup_audio(file_path)
            update_job(job_id, status=JobStatus.ERROR, message="Cancelled", stage="Cancelled")
            return

        # Transcribe
        update_job(job_id, status=JobStatus.TRANSCRIBING, progress=45, stage="Starting transcription...")
        text = transcribe_audio(file_path, api_key, transcribe_progress)

        # Check cancellation
        if is_cancelled(job_id):
            if file_path:
                cleanup_audio(file_path)
            update_job(job_id, status=JobStatus.ERROR, message="Cancelled", stage="Cancelled")
            return

        # Saving
        update_job(job_id, status=JobStatus.SAVING, progress=92, stage="Saving transcription...")
        saved_path = save_transcription(title, text)

        # Add to history
        add_to_history(
            id=job_id,
            title=title,
            url=url,
            file_path=saved_path,
            duration=video_info.get('duration'),
            thumbnail=video_info.get('thumbnail')
        )

        # Cleanup
        cleanup_audio(file_path)

        # Complete
        update_job(
            job_id,
            status=JobStatus.COMPLETE,
            progress=100,
            stage="Complete!",
            text=text,
            file_path=saved_path
        )

    except Exception as e:
        error_msg = str(e)
        if error_msg == "Cancelled":
            update_job(job_id, status=JobStatus.ERROR, message="Cancelled", stage="Cancelled")
        else:
            update_job(job_id, status=JobStatus.ERROR, message=error_msg, stage="Error")
        # Cleanup on error
        if file_path:
            try:
                cleanup_audio(file_path)
            except:
                pass

    # Clean up cancelled status
    with cancelled_lock:
        cancelled_jobs.discard(job_id)

    # Process next in queue
    process_queue()


def process_queue():
    """Process the next item in the queue."""
    global queue_processing

    with queue_lock:
        if not job_queue:
            queue_processing = False
            return

        if queue_processing:
            return

        queue_processing = True
        job_id, url = job_queue.popleft()

    thread = Thread(target=process_job, args=(job_id, url))
    thread.start()


# Video Preview
@router.post("/preview", response_model=VideoPreview)
async def preview_video(request: VideoPreviewRequest):
    """Fetch video metadata without downloading."""
    try:
        info = get_video_info(request.url)
        return VideoPreview(
            url=request.url,
            video_id=info['video_id'],
            title=info['title'],
            thumbnail=info['thumbnail'],
            duration=info['duration'],
            channel=info['channel']
        )
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


# Transcription
@router.post("/transcribe", response_model=TranscribeResponse)
async def create_transcription(request: TranscribeRequest):
    """Start a new transcription job."""
    api_key = get_api_key()
    if not api_key:
        raise HTTPException(
            status_code=400,
            detail="OpenAI API key not configured. Please set it in Settings."
        )

    job_id = str(uuid.uuid4())
    with jobs_lock:
        jobs[job_id] = {
            "status": JobStatus.PENDING,
            "progress": 0,
            "stage": "Queued",
            "title": "",
            "text": "",
            "message": None,
            "file_path": None,
            "video_info": None,
        }

    thread = Thread(target=process_job, args=(job_id, request.url))
    thread.start()

    return TranscribeResponse(job_id=job_id, status=JobStatus.PENDING)


# Queue/Batch
@router.post("/queue", response_model=QueueStatusResponse)
async def add_to_queue(request: QueueRequest):
    """Add multiple URLs to the transcription queue."""
    api_key = get_api_key()
    if not api_key:
        raise HTTPException(status_code=400, detail="OpenAI API key not configured")

    job_ids = []
    with queue_lock:
        for url in request.urls:
            job_id = str(uuid.uuid4())
            with jobs_lock:
                jobs[job_id] = {
                    "status": JobStatus.PENDING,
                    "progress": 0,
                    "stage": "Queued",
                    "title": "",
                    "text": "",
                    "message": None,
                    "file_path": None,
                    "video_info": None,
                    "url": url,
                }
            job_queue.append((job_id, url))
            job_ids.append({"job_id": job_id, "url": url})

    # Start processing if not already
    process_queue()

    return QueueStatusResponse(
        queue=job_ids,
        current_job=job_ids[0]["job_id"] if job_ids else None,
        total=len(job_ids),
        completed=0
    )


@router.get("/queue", response_model=QueueStatusResponse)
async def get_queue_status():
    """Get current queue status."""
    with queue_lock:
        queue_list = [{"job_id": jid, "url": url} for jid, url in job_queue]

    with jobs_lock:
        current = None
        completed = 0
        for jid, job in jobs.items():
            if job["status"] == JobStatus.COMPLETE:
                completed += 1
            elif job["status"] not in (JobStatus.PENDING, JobStatus.ERROR, JobStatus.COMPLETE):
                current = jid

    return QueueStatusResponse(
        queue=queue_list,
        current_job=current,
        total=len(queue_list),
        completed=completed
    )


# Status
@router.get("/status/{job_id}", response_model=StatusResponse)
async def get_status(job_id: str):
    """Get the status of a transcription job."""
    with jobs_lock:
        if job_id not in jobs:
            raise HTTPException(status_code=404, detail="Job not found")
        job = jobs[job_id].copy()

    return StatusResponse(
        status=job["status"],
        progress=job["progress"],
        message=job.get("message"),
        stage=job.get("stage")
    )


# Cancel
@router.post("/cancel/{job_id}")
async def cancel_job(job_id: str):
    """Cancel a running transcription job."""
    with jobs_lock:
        if job_id not in jobs:
            raise HTTPException(status_code=404, detail="Job not found")
        job = jobs[job_id]
        if job["status"] in (JobStatus.COMPLETE, JobStatus.ERROR):
            raise HTTPException(status_code=400, detail="Job already finished")

    # Mark as cancelled
    with cancelled_lock:
        cancelled_jobs.add(job_id)

    # Update job status immediately for UI feedback
    update_job(job_id, stage="Cancelling...")

    return {"success": True, "message": "Cancellation requested"}


# Result
@router.get("/result/{job_id}", response_model=ResultResponse)
async def get_result(job_id: str):
    """Get the result of a completed transcription job."""
    with jobs_lock:
        if job_id not in jobs:
            raise HTTPException(status_code=404, detail="Job not found")
        job = jobs[job_id].copy()

    if job["status"] != JobStatus.COMPLETE:
        raise HTTPException(status_code=400, detail="Job not complete")

    return ResultResponse(
        text=job["text"],
        title=job["title"],
        file_path=job.get("file_path")
    )


# History
@router.get("/history", response_model=HistoryResponse)
async def get_history_list(search: str = "", limit: int = 50, offset: int = 0):
    """Get transcription history."""
    items, total = get_history(search=search or None, limit=limit, offset=offset)
    return HistoryResponse(
        items=[HistoryItem(**item) for item in items],
        total=total
    )


@router.get("/history/{item_id}")
async def get_history_detail(item_id: str):
    """Get a specific history item with full text."""
    item = get_history_item(item_id)
    if not item:
        raise HTTPException(status_code=404, detail="Item not found")

    text = get_transcription_text(item['file_path'])
    return {**item, "text": text}


@router.delete("/history/{item_id}")
async def delete_history(item_id: str):
    """Delete a history item."""
    if delete_history_item(item_id):
        return {"success": True}
    raise HTTPException(status_code=404, detail="Item not found")


@router.delete("/history")
async def clear_all_history():
    """Clear all history."""
    clear_history()
    return {"success": True}


# Export
@router.get("/export/{item_id}")
async def export_transcription(item_id: str, format: ExportFormat = ExportFormat.TXT):
    """Export a transcription in various formats."""
    # Check in active jobs first
    with jobs_lock:
        if item_id in jobs and jobs[item_id]["status"] == JobStatus.COMPLETE:
            text = jobs[item_id]["text"]
            title = jobs[item_id]["title"]
        else:
            # Check history
            item = get_history_item(item_id)
            if not item:
                raise HTTPException(status_code=404, detail="Item not found")
            text = get_transcription_text(item['file_path'])
            title = item['title']
            if not text:
                raise HTTPException(status_code=404, detail="Transcription file not found")

    safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).strip()[:50]

    if format == ExportFormat.TXT:
        return PlainTextResponse(
            content=text,
            headers={"Content-Disposition": f'attachment; filename="{safe_title}.txt"'}
        )
    elif format == ExportFormat.SRT:
        srt_content = text_to_srt(text, title)
        return PlainTextResponse(
            content=srt_content,
            headers={"Content-Disposition": f'attachment; filename="{safe_title}.srt"'}
        )
    elif format == ExportFormat.VTT:
        vtt_content = text_to_vtt(text, title)
        return PlainTextResponse(
            content=vtt_content,
            headers={"Content-Disposition": f'attachment; filename="{safe_title}.vtt"'}
        )
    else:
        raise HTTPException(status_code=400, detail="Unsupported format")


# AI Analysis
@router.post("/analyze/{item_id}")
async def analyze_content(item_id: str, analysis_type: str = "full"):
    """
    Analyze transcription with AI.

    analysis_type can be: "full", "summary", "takeaways", or "topics"
    Results are cached in history to avoid re-analyzing.
    """
    api_key = get_api_key()
    if not api_key:
        raise HTTPException(status_code=400, detail="OpenAI API key not configured")

    # Get history item and check for cached analysis
    item = get_history_item(item_id)
    title = ""
    if item:
        title = item.get('title', '')
        # Return cached analysis if available and full analysis requested
        if analysis_type == "full" and item.get('analysis'):
            return item['analysis']

    # Get transcription text
    with jobs_lock:
        if item_id in jobs and jobs[item_id]["status"] == JobStatus.COMPLETE:
            text = jobs[item_id]["text"]
            title = jobs[item_id].get("title", title)
        else:
            if not item:
                raise HTTPException(status_code=404, detail="Item not found")
            text = get_transcription_text(item['file_path'])
            if not text:
                raise HTTPException(status_code=404, detail="Transcription file not found")

    if not text or len(text.strip()) < 50:
        raise HTTPException(status_code=400, detail="Transcription too short to analyze")

    try:
        result = analyze_transcription(text, api_key, analysis_type)

        # Save analysis to history and topics database
        if analysis_type == "full":
            update_history_item(item_id, analysis=result)
            # Save topics to topics database
            if result.get('topics'):
                add_topics_to_video(item_id, result['topics'], title)

        return result
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}")


# Translation
@router.post("/translate/{item_id}")
async def translate_content(item_id: str, target_language: str = "English"):
    """
    Translate transcription to a target language.
    Translations are cached in history to avoid re-translating.

    Args:
        item_id: Job ID or history item ID
        target_language: Target language for translation (e.g., "English", "Spanish", "French")
    """
    api_key = get_api_key()
    if not api_key:
        raise HTTPException(status_code=400, detail="OpenAI API key not configured")

    # Check for cached translation
    item = get_history_item(item_id)
    if item:
        translations = item.get('translations', {})
        if target_language in translations:
            return {"translation": translations[target_language], "target_language": target_language, "cached": True}

    # Get transcription text
    with jobs_lock:
        if item_id in jobs and jobs[item_id]["status"] == JobStatus.COMPLETE:
            text = jobs[item_id]["text"]
        else:
            if not item:
                raise HTTPException(status_code=404, detail="Item not found")
            text = get_transcription_text(item['file_path'])
            if not text:
                raise HTTPException(status_code=404, detail="Transcription file not found")

    if not text or len(text.strip()) < 10:
        raise HTTPException(status_code=400, detail="Transcription too short to translate")

    try:
        translated = translate_text(text, api_key, target_language)

        # Cache translation in history
        if item:
            translations = item.get('translations', {})
            translations[target_language] = translated
            update_history_item(item_id, translations=translations)

        return {"translation": translated, "target_language": target_language}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Translation failed: {str(e)}")


# Format with AI
@router.post("/format/{item_id}")
async def format_content(item_id: str):
    """
    Format transcription with headers and better structure.
    Organizes content under logical section headers while preserving all text.
    Results are cached in history.
    """
    api_key = get_api_key()
    if not api_key:
        raise HTTPException(status_code=400, detail="OpenAI API key not configured")

    # Check for cached formatted text
    item = get_history_item(item_id)
    if item and item.get('formatted_text'):
        return {"formatted_text": item['formatted_text'], "cached": True}

    # Get transcription text
    with jobs_lock:
        if item_id in jobs and jobs[item_id]["status"] == JobStatus.COMPLETE:
            text = jobs[item_id]["text"]
        else:
            if not item:
                raise HTTPException(status_code=404, detail="Item not found")
            text = get_transcription_text(item['file_path'])
            if not text:
                raise HTTPException(status_code=404, detail="Transcription file not found")

    if not text or len(text.strip()) < 50:
        raise HTTPException(status_code=400, detail="Transcription too short to format")

    try:
        formatted = format_transcription(text, api_key)

        # Cache formatted text in history
        if item:
            update_history_item(item_id, formatted_text=formatted)

        return {"formatted_text": formatted}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Formatting failed: {str(e)}")


# Topics API
@router.get("/topics")
async def list_topics(search: str = ""):
    """Get all topics or search topics."""
    if search:
        return {"topics": search_topics(search)}
    return {"topics": get_all_topics()}


@router.get("/topics/{topic}/videos")
async def get_topic_videos(topic: str):
    """Get all videos for a specific topic."""
    video_ids = get_videos_by_topic(topic)
    # Get video details from history
    videos = []
    for vid in video_ids:
        item = get_history_item(vid)
        if item:
            videos.append({
                "id": item['id'],
                "title": item['title'],
                "thumbnail": item.get('thumbnail'),
                "created_at": item.get('created_at'),
                "duration": item.get('duration')
            })
    return {"topic": topic, "videos": videos, "count": len(videos)}


@router.get("/history/{item_id}/topics")
async def get_item_topics(item_id: str):
    """Get topics for a specific video."""
    topics = get_video_topics(item_id)
    return {"video_id": item_id, "topics": topics}


@router.post("/history/{item_id}/topics")
async def add_item_topics(item_id: str, topics: List[str]):
    """Add topics to a video (manual tagging)."""
    item = get_history_item(item_id)
    if not item:
        raise HTTPException(status_code=404, detail="Item not found")

    add_topics_to_video(item_id, topics, item.get('title', ''))
    return {"success": True, "topics": get_video_topics(item_id)}


@router.delete("/history/{item_id}/topics/{topic}")
async def remove_item_topic(item_id: str, topic: str):
    """Remove a topic from a video."""
    if remove_topic_from_video(item_id, topic):
        return {"success": True}
    raise HTTPException(status_code=404, detail="Topic not found for this video")


@router.get("/history/{item_id}/related")
async def get_related_items(item_id: str, limit: int = 5):
    """Get videos related to this one based on shared topics."""
    related = get_related_videos(item_id, limit)
    # Enrich with video details
    for item in related:
        history_item = get_history_item(item['video_id'])
        if history_item:
            item['title'] = history_item.get('title', item.get('title', ''))
            item['thumbnail'] = history_item.get('thumbnail')
            item['duration'] = history_item.get('duration')
    return {"video_id": item_id, "related": related}


# Downloadable Package
@router.get("/package/{item_id}")
async def get_transcription_package(item_id: str):
    """
    Get a complete package of all transcription data including:
    - Transcription text
    - Analysis (summary, takeaways, topics)
    - Translations
    - Video metadata
    """
    item = get_history_item(item_id)
    if not item:
        # Check active jobs
        with jobs_lock:
            if item_id in jobs and jobs[item_id]["status"] == JobStatus.COMPLETE:
                job = jobs[item_id]
                return {
                    "id": item_id,
                    "title": job.get("title", ""),
                    "transcription": job.get("text", ""),
                    "analysis": None,
                    "translations": {},
                    "topics": [],
                    "metadata": {
                        "video_info": job.get("video_info"),
                    }
                }
        raise HTTPException(status_code=404, detail="Item not found")

    # Get transcription text
    text = get_transcription_text(item['file_path'])

    # Get topics
    topics = get_video_topics(item_id)

    # Get related videos
    related = get_related_videos(item_id, 5)

    return {
        "id": item_id,
        "title": item.get('title', ''),
        "url": item.get('url', ''),
        "transcription": text,
        "analysis": item.get('analysis'),
        "translations": item.get('translations', {}),
        "topics": topics,
        "related_videos": related,
        "metadata": {
            "created_at": item.get('created_at'),
            "duration": item.get('duration'),
            "thumbnail": item.get('thumbnail'),
        }
    }
