"""
Data reader for Video Viewer Bot.
Read-only access to /mnt/video data.
"""

import json
import os
import re
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Optional

NFS_BASE = os.getenv("NFS_BASE", "/mnt/video")

# Known marketplace directory prefixes
_KNOWN_PREFIXES = ("wb_", "avito_", "ozon_")


def _parse_product_dir(name: str) -> Optional[tuple]:
    """Parse product directory name into (prefix, product_id) or None."""
    for pfx in _KNOWN_PREFIXES:
        if name.startswith(pfx):
            return pfx.rstrip("_"), name[len(pfx):]
    return None


@dataclass
class ProductInfo:
    product_id: str
    product_name: Optional[str]
    generation_count: int
    successful_count: int
    last_generated: Optional[datetime]
    ratings: dict  # {"good": N, "bad": N}
    source: str = "wb"  # marketplace prefix: 'wb', 'avito', 'ozon'


@dataclass
class GenerationInfo:
    task_id: str
    product_id: str
    product_name: Optional[str]
    started_at: Optional[datetime]
    completed_at: Optional[datetime]
    has_video: bool
    video_path: Optional[str]
    video_uri: Optional[str]
    errors: list
    rating: Optional[str]
    comments: list
    story_vision: Optional[dict]
    veo3_prompt: Optional[str]
    frame_prompts: Optional[dict]  # {"frame_act1": ..., "frame_act2": ...}


def list_products() -> list[ProductInfo]:
    """List all products with basic stats."""
    products = []
    base = Path(NFS_BASE)

    for d in sorted(base.iterdir()):
        parsed = _parse_product_dir(d.name)
        if not parsed:
            continue

        source, product_id = parsed
        product_name = None
        generation_count = 0
        successful_count = 0
        last_generated = None
        ratings = {"good": 0, "bad": 0}

        # Parse activity.log for ratings
        activity_log = d / "activity.log"
        if activity_log.exists():
            for line in activity_log.read_text().splitlines():
                if " | RATE | " in line:
                    parts = line.split(" | ")
                    if len(parts) >= 5:
                        rating = parts[4].strip().lower()
                        if rating in ratings:
                            ratings[rating] += 1

        # Scan generation directories
        for gen_dir in d.iterdir():
            if not gen_dir.is_dir() or gen_dir.name.startswith("_"):
                continue

            # Try to parse timestamp from directory name (YYYYMMDD_HHMMSS_xxxx)
            if not re.match(r"\d{8}_\d{6}_\w+", gen_dir.name):
                continue

            generation_count += 1

            # Check for video
            video_path = gen_dir / "videos" / "video.mp4"
            if video_path.exists():
                successful_count += 1

            # Get product name and timestamp from metadata
            metadata_file = gen_dir / "metadata.json"
            if metadata_file.exists():
                try:
                    meta = json.loads(metadata_file.read_text())
                    if not product_name and meta.get("product_name"):
                        product_name = meta["product_name"]
                    if meta.get("started_at"):
                        ts = datetime.fromisoformat(meta["started_at"])
                        if not last_generated or ts > last_generated:
                            last_generated = ts
                except (json.JSONDecodeError, ValueError):
                    pass

        if generation_count > 0:
            products.append(ProductInfo(
                product_id=product_id,
                product_name=product_name,
                generation_count=generation_count,
                successful_count=successful_count,
                last_generated=last_generated,
                ratings=ratings,
                source=source,
            ))

    # Sort by last generated (most recent first)
    products.sort(key=lambda p: p.last_generated or datetime.min, reverse=True)
    return products


def _find_product_dir(product_id: str, source: str = None) -> Optional[Path]:
    """Find product directory, optionally scoped to a specific marketplace."""
    base = Path(NFS_BASE)
    if source:
        candidate = base / f"{source}_{product_id}"
        return candidate if candidate.is_dir() else None
    # No source specified — search all prefixes, prefer dirs with generations
    fallback = None
    for pfx in _KNOWN_PREFIXES:
        candidate = base / f"{pfx}{product_id}"
        if candidate.is_dir():
            has_gens = any(
                d.is_dir() and re.match(r"\d{8}_\d{6}_\w+", d.name)
                for d in candidate.iterdir()
            )
            if has_gens:
                return candidate
            if fallback is None:
                fallback = candidate
    return fallback


def get_product_generations(product_id: str, source: str = None) -> list[GenerationInfo]:
    """Get all generations for a product."""
    product_dir = _find_product_dir(product_id, source)
    if not product_dir:
        return []

    # Parse activity.log for ratings and comments
    ratings = {}  # task_id -> rating
    comments = {}  # task_id -> [comments]

    activity_log = product_dir / "activity.log"
    if activity_log.exists():
        for line in activity_log.read_text().splitlines():
            parts = line.split(" | ")
            if len(parts) >= 4:
                action = parts[2].strip()
                task_id = parts[3].strip()

                if action == "RATE" and len(parts) >= 5:
                    ratings[task_id] = parts[4].strip().lower()
                elif action == "COMMENT" and len(parts) >= 5:
                    if task_id not in comments:
                        comments[task_id] = []
                    comments[task_id].append(parts[4].strip())

    generations = []

    for gen_dir in product_dir.iterdir():
        if not gen_dir.is_dir() or gen_dir.name.startswith("_"):
            continue
        if not re.match(r"\d{8}_\d{6}_\w+", gen_dir.name):
            continue

        task_id = gen_dir.name
        gen_info = _parse_generation(gen_dir, task_id, product_id)
        gen_info.rating = ratings.get(task_id)
        gen_info.comments = comments.get(task_id, [])
        generations.append(gen_info)

    # Sort by started_at (most recent first)
    generations.sort(key=lambda g: g.started_at or datetime.min, reverse=True)
    return generations


def get_generation(task_id: str) -> Optional[GenerationInfo]:
    """Get a specific generation by task_id."""
    # Search all products for this task_id
    base = Path(NFS_BASE)

    for product_dir in base.iterdir():
        parsed = _parse_product_dir(product_dir.name)
        if not parsed:
            continue

        gen_dir = product_dir / task_id
        if gen_dir.exists() and gen_dir.is_dir():
            _source, product_id = parsed
            gen_info = _parse_generation(gen_dir, task_id, product_id)

            # Get rating and comments from activity.log
            activity_log = product_dir / "activity.log"
            if activity_log.exists():
                for line in activity_log.read_text().splitlines():
                    parts = line.split(" | ")
                    if len(parts) >= 4 and parts[3].strip() == task_id:
                        action = parts[2].strip()
                        if action == "RATE" and len(parts) >= 5:
                            gen_info.rating = parts[4].strip().lower()
                        elif action == "COMMENT" and len(parts) >= 5:
                            gen_info.comments.append(parts[4].strip())

            return gen_info

    return None


def _parse_generation(gen_dir: Path, task_id: str, product_id: str) -> GenerationInfo:
    """Parse generation info from directory."""
    product_name = None
    started_at = None
    completed_at = None
    video_uri = None
    errors = []
    story_vision = None
    veo3_prompt = None

    # Check for video
    video_path = gen_dir / "videos" / "video.mp4"
    has_video = video_path.exists()

    # Parse timestamp from task_id as fallback (YYYYMMDD_HHMMSS_xxxx)
    # This ensures in-progress generations sort correctly even before metadata.json exists
    match = re.match(r"(\d{8})_(\d{6})_\w+", task_id)
    if match:
        date_str, time_str = match.groups()
        try:
            started_at = datetime.strptime(f"{date_str}_{time_str}", "%Y%m%d_%H%M%S")
        except ValueError:
            pass

    # Read metadata.json
    metadata_file = gen_dir / "metadata.json"
    if metadata_file.exists():
        try:
            meta = json.loads(metadata_file.read_text())
            product_name = meta.get("product_name")
            # Override with metadata timestamp if available (more accurate)
            if meta.get("started_at"):
                started_at = datetime.fromisoformat(meta["started_at"])
            if meta.get("completed_at"):
                completed_at = datetime.fromisoformat(meta["completed_at"])
            video_uri = meta.get("video_uri")
            errors = meta.get("errors", [])

            # Try to get veo3_prompt from prompts.creative
            prompts = meta.get("prompts", {})
            creative = prompts.get("creative", {})
            veo3_prompt = creative.get("veo3_prompt")
        except (json.JSONDecodeError, ValueError):
            pass

    # Read generation.log for story_vision and frame_prompts
    frame_prompts = None
    gen_log = gen_dir / "generation.log"
    if gen_log.exists():
        try:
            log_data = json.loads(gen_log.read_text())
            stages = log_data.get("stages", {})
            stage2 = stages.get("stage2", {})
            story_vision = stage2.get("story_vision")

            # Get frame prompts from stage3
            stage3 = stages.get("stage3", {})
            frame_prompts = stage3.get("frame_prompts")

            # Fallback: get veo3_prompt from stage4
            if not veo3_prompt:
                stage4 = stages.get("stage4", {})
                veo3_prompt = stage4.get("veo3_prompt")
        except (json.JSONDecodeError, ValueError):
            pass

    return GenerationInfo(
        task_id=task_id,
        product_id=product_id,
        product_name=product_name,
        started_at=started_at,
        completed_at=completed_at,
        has_video=has_video,
        video_path=str(video_path) if has_video else None,
        video_uri=video_uri,
        errors=errors,
        rating=None,
        comments=[],
        story_vision=story_vision,
        veo3_prompt=veo3_prompt,
        frame_prompts=frame_prompts
    )


def get_stats() -> dict:
    """Get aggregate statistics."""
    products = list_products()

    total_generations = sum(p.generation_count for p in products)
    total_successful = sum(p.successful_count for p in products)
    total_good = sum(p.ratings.get("good", 0) for p in products)
    total_bad = sum(p.ratings.get("bad", 0) for p in products)

    return {
        "products_count": len(products),
        "total_generations": total_generations,
        "total_successful": total_successful,
        "total_failed": total_generations - total_successful,
        "ratings": {"good": total_good, "bad": total_bad},
        "success_rate": round(total_successful / total_generations * 100, 1) if total_generations else 0
    }


def write_feedback(task_id: str, user_id: int, action: str, value: str) -> bool:
    """Write feedback to product's activity.log."""
    gen = get_generation(task_id)
    if not gen:
        return False

    product_dir = _find_product_dir(gen.product_id)
    if not product_dir:
        return False
    activity_log = product_dir / "activity.log"

    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"{timestamp} | user:{user_id} | {action} | {task_id} | {value}\n"

    with open(activity_log, "a") as f:
        f.write(line)

    return True


def log_viewer_action(user_id: int, action: str, details: str = ""):
    """Log action to viewer's activity log."""
    log_path = Path(NFS_BASE) / "_viewer" / "viewer_activity.log"
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"{timestamp} | user:{user_id} | {action} | {details}\n"

    with open(log_path, "a") as f:
        f.write(line)
