import os
from datetime import datetime, UTC

from loguru import logger

from ...config.paths import get_data_directory
from ...database.models import ResearchLog
from ...database.session_context import get_user_db_session

# Database paths using new centralized configuration
_raw_data_dir = get_data_directory()
DATA_DIR: str | None = None
if _raw_data_dir:
    DATA_DIR = str(_raw_data_dir)
    os.makedirs(DATA_DIR, exist_ok=True)

# DB_PATH removed - use per-user encrypted databases instead


def get_db_connection():
    """
    Get a connection to the SQLite database.
    DEPRECATED: This uses the shared database which should not be used.
    Use get_db_session() instead for per-user databases.
    """
    raise RuntimeError(
        "Shared database access is deprecated. Use get_db_session() for per-user databases."
    )


def calculate_duration(created_at_str, completed_at_str=None):
    """
    Calculate duration in seconds between created_at timestamp and completed_at or now.
    Handles various timestamp formats and returns None if calculation fails.

    Args:
        created_at_str: The start timestamp
        completed_at_str: Optional end timestamp, defaults to current time if None

    Returns:
        Duration in seconds or None if calculation fails
    """
    if not created_at_str:
        return None

    end_time = None
    if completed_at_str:
        # Use completed_at time if provided
        try:
            if "T" in completed_at_str:  # ISO format with T separator
                end_time = datetime.fromisoformat(completed_at_str)
            else:  # Older format without T
                # Try different formats
                try:
                    end_time = datetime.strptime(
                        completed_at_str, "%Y-%m-%d %H:%M:%S.%f"
                    )
                except ValueError:
                    try:
                        end_time = datetime.strptime(
                            completed_at_str, "%Y-%m-%d %H:%M:%S"
                        )
                    except ValueError:
                        # Last resort fallback
                        end_time = datetime.fromisoformat(
                            completed_at_str.replace(" ", "T")
                        )
        except Exception:
            logger.exception("Error parsing completed_at timestamp")
            try:
                from dateutil import parser  # type: ignore[import-untyped]

                end_time = parser.parse(completed_at_str)
            except Exception:
                logger.exception(
                    f"Fallback parsing also failed for completed_at: {completed_at_str}"
                )
                # Fall back to current time
                end_time = datetime.now(UTC)
    else:
        # Use current time if no completed_at provided
        end_time = datetime.now(UTC)
    # Ensure end_time is UTC.
    end_time = end_time.astimezone(UTC)

    start_time = None
    try:
        # Proper parsing of ISO format
        if "T" in created_at_str:  # ISO format with T separator
            start_time = datetime.fromisoformat(created_at_str)
        else:  # Older format without T
            # Try different formats
            try:
                start_time = datetime.strptime(
                    created_at_str, "%Y-%m-%d %H:%M:%S.%f"
                )
            except ValueError:
                try:
                    start_time = datetime.strptime(
                        created_at_str, "%Y-%m-%d %H:%M:%S"
                    )
                except ValueError:
                    # Last resort fallback
                    start_time = datetime.fromisoformat(
                        created_at_str.replace(" ", "T")
                    )
    except Exception:
        logger.exception("Error parsing created_at timestamp")
        # Fallback method if parsing fails
        try:
            from dateutil import parser

            start_time = parser.parse(created_at_str)
        except Exception:
            logger.exception(
                f"Fallback parsing also failed for created_at: {created_at_str}"
            )
            return None

    # Calculate duration if both timestamps are valid
    if start_time and end_time:
        try:
            return int((end_time - start_time).total_seconds())
        except Exception:
            logger.exception("Error calculating duration")

    return None


def get_logs_for_research(research_id, limit: int | None = None):
    """
    Retrieve logs for a specific research ID, oldest-first.

    Args:
        research_id: ID of the research
        limit: If set, return only the most recent ``limit`` entries
            (still oldest-first in the returned list). Used to bound the
            response size of ``/history/logs/<id>`` — long langgraph
            researches can produce thousands of 10 KB rows that the
            frontend prunes to 500 anyway.

    Returns:
        List of log entries as dictionaries
    """
    try:
        with get_user_db_session() as session:
            query = session.query(ResearchLog).filter(
                ResearchLog.research_id == research_id
            )
            if limit is not None:
                # Take the newest ``limit`` rows from the DB, then flip back
                # to oldest-first ordering for the caller. Avoids loading
                # the entire table when only the tail is wanted. ``id`` is the
                # tie-break: timestamps are not unique, so without it the rows
                # surviving ``.limit()`` at a shared-timestamp boundary would
                # be SQL-undefined.
                log_results = list(
                    reversed(
                        query.order_by(
                            ResearchLog.timestamp.desc(),
                            ResearchLog.id.desc(),
                        )
                        .limit(limit)
                        .all()
                    )
                )
            else:
                log_results = query.order_by(
                    ResearchLog.timestamp.asc(), ResearchLog.id.asc()
                ).all()

            logs = []
            for result in log_results:
                # Convert entry for frontend consumption
                formatted_entry = {
                    "time": result.timestamp,
                    "message": result.message,
                    "type": result.level,
                    "module": result.module,
                    "line_no": result.line_no,
                }
                logs.append(formatted_entry)

            return logs
    except Exception:
        logger.exception("Error retrieving logs from database")
        return []


@logger.catch
def get_total_logs_for_research(research_id):
    """
    Returns the total number of logs for a given `research_id`.

    Args:
        research_id (int): The ID of the research.

    Returns:
        int: Total number of logs for the specified research ID.
    """
    with get_user_db_session() as session:
        return (
            session.query(ResearchLog)
            .filter(ResearchLog.research_id == research_id)
            .count()
        )
