"""File listing, filtering, and pagination with DynamoDB."""

import logging
import math
import os
from typing import TYPE_CHECKING, Any

try:
    import boto3

    HAS_BOTO3 = True
except ImportError:
    boto3 = None  # type: ignore[assignment]
    HAS_BOTO3 = False

if TYPE_CHECKING:
    from mypy_boto3_dynamodb.service_resource import Table

logger = logging.getLogger(__name__)

ALLOWED_SORT_COLUMNS = {"filename", "created_at", "status", "total_rows", "credits_charged"}
ALLOWED_STATUSES = {"completed", "processing", "failed", "pending"}

SORT_KEY_MAP: dict[str, Any] = {
    "filename": lambda item: (item.get("file_name") or item.get("filename") or "").lower(),
    "created_at": lambda item: item.get("created_at", ""),
    "status": lambda item: item.get("status", ""),
    "total_rows": lambda item: int(item.get("total_rows", 0) or item.get("row_count", 0) or 0),
    "credits_charged": lambda item: int(item.get("credits_charged", 0) or 0),
}


def _get_filename(item: dict[str, Any]) -> str:
    """Get filename from item, handling field name inconsistencies."""
    return item.get("file_name") or item.get("filename") or ""


def _map_file_fields(item: dict[str, Any]) -> dict[str, Any]:
    """Map DynamoDB item fields to frontend field names."""
    filename = _get_filename(item)
    status = item.get("status", "")
    total_records = int(item.get("total_rows", 0) or item.get("row_count", 0) or 0)

    file_url = item.get("result_url") or item.get("result_s3_url") or ""
    if status == "failed":
        file_url = "Failed"

    mapped = {
        "file_reference": item.get("file_id", ""),
        "file_name": filename,
        "last_updated": item.get("created_at", ""),
        "total_records": total_records,
        "file_url": file_url,
        "status": status,
        "processed_rows": int(item.get("processed_rows", 0) or 0),
        "credits_charged": int(item.get("credits_charged", 0) or 0),
        "credits_reversed": int(item.get("credits_reversed", 0) or 0),
        "file_method": item.get("file_method", ""),
    }

    if status == "completed":
        mapped["completed_at"] = item.get("completed_at", "")
        mapped["result_available"] = bool(file_url and file_url != "Failed")

    if status == "failed":
        mapped["error"] = item.get("error", "")
        mapped["failed_at"] = item.get("failed_at", "")

    return mapped


def _map_admin_fields(item: dict[str, Any]) -> dict[str, Any]:
    """Map DynamoDB item fields for admin view."""
    return {
        "file_id": item.get("file_id", ""),
        "filename": _get_filename(item),
        "created_at": item.get("created_at") or item.get("uploaded_at", ""),
        "row_count": int(item.get("total_rows", 0) or item.get("row_count", 0) or 0),
        "status": item.get("status", ""),
        "input_key": item.get("input_key") or item.get("s3_key"),
        "output_key": item.get("output_key") or item.get("result_key"),
        "input_url": None,
        "output_url": None,
    }


class FileService:
    """Manages file metadata listing with filtering and pagination.

    DynamoDB table schema:
        - Hash key: file_id (S)
        - GSI: user-index on user_id (HASH) / created_at (RANGE)
    """

    table: "Table | None"

    def __init__(self, table_name: str | None = None):
        """Initialize with DynamoDB table."""
        if not HAS_BOTO3 or not boto3:
            raise RuntimeError("boto3 is required for FileService")

        self.dynamodb = boto3.resource("dynamodb")
        self.table_name = table_name if table_name else os.environ["FILES_TABLE"]

        try:
            self.table = self.dynamodb.Table(self.table_name)
        except Exception as e:
            logger.error(f"Failed to connect to DynamoDB table {self.table_name}: {e}")
            self.table = None

    def _query_all_user_files(self, user_id: str) -> list[dict[str, Any]]:
        """Query all files for a user via the user-index GSI."""
        if not self.table:
            raise RuntimeError(f"DynamoDB table {self.table_name} not accessible")

        items: list[dict[str, Any]] = []
        kwargs: dict[str, Any] = {
            "IndexName": "user-index",
            "KeyConditionExpression": "user_id = :uid",
            "ExpressionAttributeValues": {":uid": user_id},
        }

        while True:
            response = self.table.query(**kwargs)
            items.extend(response.get("Items", []))
            last_key = response.get("LastEvaluatedKey")
            if not last_key:
                break
            kwargs["ExclusiveStartKey"] = last_key

        return items

    def list_user_files(
        self,
        user_id: str,
        page: int = 1,
        limit: int = 20,
        search: str = "",
        sort_by: str = "created_at",
        sort_order: str = "desc",
        status_filter: str = "",
    ) -> dict[str, Any]:
        """List files for a user with filtering, sorting, and pagination.

        Args:
            user_id: User identifier.
            page: Page number (1-indexed).
            limit: Items per page.
            search: Case-insensitive substring match on filename.
            sort_by: Column to sort by (from ALLOWED_SORT_COLUMNS).
            sort_order: 'asc' or 'desc'.
            status_filter: Filter by file status.

        Returns:
            Dict with files list and pagination metadata.
        """
        items = self._query_all_user_files(user_id)

        # Apply status filter
        if status_filter and status_filter in ALLOWED_STATUSES:
            items = [i for i in items if i.get("status") == status_filter]

        # Apply search filter
        if search:
            search_lower = search.lower()
            items = [i for i in items if search_lower in _get_filename(i).lower()]

        # Sort
        sort_key = SORT_KEY_MAP.get(sort_by, SORT_KEY_MAP["created_at"])
        reverse = sort_order != "asc"
        items.sort(key=sort_key, reverse=reverse)

        # Pagination
        total_count = len(items)
        total_pages = max(1, math.ceil(total_count / limit))
        start = (page - 1) * limit
        end = start + limit
        page_items = items[start:end]

        return {
            "files": [_map_file_fields(item) for item in page_items],
            "page": page,
            "per_page": limit,
            "has_more": end < total_count,
            "total_pages": total_pages,
            "total_files_count": total_count,
        }

    def list_user_files_admin(
        self,
        user_id: str,
        page: int = 1,
        limit: int = 20,
    ) -> dict[str, Any]:
        """Admin view of user files with raw S3 keys.

        Returns raw S3 keys for the handler to generate presigned URLs.
        """
        items = self._query_all_user_files(user_id)

        # Sort newest first
        items.sort(key=lambda i: i.get("created_at", ""), reverse=True)

        total = len(items)
        start = (page - 1) * limit
        end = start + limit
        page_items = items[start:end]

        return {
            "files": [_map_admin_fields(item) for item in page_items],
            "total": total,
            "page": page,
            "limit": limit,
        }
