"""SQLite database operations for Todo CLI."""

import sqlite3
import json
from contextlib import contextmanager
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional

from .models import Todo, Priority, Status
from .config import get_config
from .migrations import MigrationManager


class Database:
    """SQLite database manager for todos."""

    def __init__(self, db_path: Optional[Path] = None):
        if db_path is None:
            db_path = get_config().get_db_path()

        db_path.parent.mkdir(parents=True, exist_ok=True)
        self.db_path = db_path
        self._init_db()

    @contextmanager
    def _get_conn(self):
        """Get database connection as context manager that auto-closes."""
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        try:
            yield conn
        finally:
            conn.close()

    def _init_db(self):
        """Initialize database schema and run migrations."""
        with self._get_conn() as conn:
            # Create base todos table (v0 schema)
            conn.execute("""
                CREATE TABLE IF NOT EXISTS todos (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    task TEXT NOT NULL,
                    priority INTEGER DEFAULT 2,
                    status TEXT DEFAULT 'todo',
                    project TEXT,
                    tags TEXT DEFAULT '[]',
                    due_date TEXT,
                    created_at TEXT NOT NULL,
                    completed_at TEXT,
                    time_spent_seconds INTEGER DEFAULT 0,
                    timer_started TEXT
                )
            """)
            conn.commit()

            # Run migrations to bring database up to current version
            migrator = MigrationManager(self.db_path)
            if migrator.needs_migration(conn):
                print(f"Migrating database from v{migrator.get_current_version(conn)} to v{migrator.CURRENT_VERSION}...")
                success = migrator.migrate(conn)
                if success:
                    print(f"Migration complete! Database is now at v{migrator.CURRENT_VERSION}")
                else:
                    print("Migration failed. Database remains at previous version.")
                    print("Your data is safe. Check error messages above.")

    def _row_to_todo(self, row: sqlite3.Row) -> Todo:
        """Convert database row to Todo object."""
        return Todo(
            id=row["id"],
            task=row["task"],
            priority=Priority(row["priority"]),
            status=Status(row["status"]),
            project=row["project"],
            project_id=row["project_id"] if "project_id" in row.keys() else None,
            tags=json.loads(row["tags"]) if row["tags"] else [],
            due_date=datetime.fromisoformat(row["due_date"]) if row["due_date"] else None,
            created_at=datetime.fromisoformat(row["created_at"]),
            completed_at=datetime.fromisoformat(row["completed_at"]) if row["completed_at"] else None,
            time_spent=timedelta(seconds=row["time_spent_seconds"]),
            timer_started=datetime.fromisoformat(row["timer_started"]) if row["timer_started"] else None,
        )

    def add(self, task: str, priority: Priority = Priority.P2,
            project: Optional[str] = None, project_id: Optional[int] = None,
            tags: list[str] = None, due_date: Optional[datetime] = None) -> Todo:
        """Add a new todo.

        Args:
            task: Task description
            priority: Task priority (P0-P3)
            project: Legacy project string (for backwards compatibility)
            project_id: Project ID (preferred method)
            tags: List of tags
            due_date: Due date

        Returns:
            Created Todo object
        """
        with self._get_conn() as conn:
            cursor = conn.execute(
                """
                INSERT INTO todos (task, priority, project, project_id, tags, due_date, created_at)
                VALUES (?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    task,
                    priority.value,
                    project,
                    project_id,
                    json.dumps(tags or []),
                    due_date.isoformat() if due_date else None,
                    datetime.now().isoformat(),
                )
            )
            conn.commit()
            return self.get(cursor.lastrowid)

    def get(self, todo_id: int) -> Optional[Todo]:
        """Get a todo by ID."""
        with self._get_conn() as conn:
            row = conn.execute(
                "SELECT * FROM todos WHERE id = ?", (todo_id,)
            ).fetchone()
            return self._row_to_todo(row) if row else None

    def list_all(self, status: Optional[Status] = None,
                 project: Optional[str] = None,
                 project_id: Optional[int] = None,
                 include_done: bool = False) -> list[Todo]:
        """List todos with optional filters.

        Args:
            status: Filter by status (todo, doing, done)
            project: Legacy project string filter (for backwards compatibility)
            project_id: Filter by project ID (preferred method)
            include_done: Include completed todos

        Returns:
            List of Todo objects matching filters
        """
        query = "SELECT * FROM todos WHERE 1=1"
        params = []

        if status:
            query += " AND status = ?"
            params.append(status.value)
        elif not include_done:
            query += " AND status != 'done'"

        # Prefer project_id over legacy project string
        if project_id is not None:
            query += " AND project_id = ?"
            params.append(project_id)
        elif project:
            # Backwards compatibility: support legacy project string
            query += " AND project = ?"
            params.append(project)

        query += " ORDER BY priority ASC, due_date ASC NULLS LAST, created_at DESC"

        with self._get_conn() as conn:
            rows = conn.execute(query, params).fetchall()
            return [self._row_to_todo(row) for row in rows]

    def update(self, todo: Todo):
        """Update a todo."""
        with self._get_conn() as conn:
            conn.execute(
                """
                UPDATE todos SET
                    task = ?, priority = ?, status = ?, project = ?,
                    tags = ?, due_date = ?, completed_at = ?,
                    time_spent_seconds = ?, timer_started = ?
                WHERE id = ?
                """,
                (
                    todo.task,
                    todo.priority.value,
                    todo.status.value,
                    todo.project,
                    json.dumps(todo.tags),
                    todo.due_date.isoformat() if todo.due_date else None,
                    todo.completed_at.isoformat() if todo.completed_at else None,
                    int(todo.time_spent.total_seconds()),
                    todo.timer_started.isoformat() if todo.timer_started else None,
                    todo.id,
                )
            )
            conn.commit()

    def delete(self, todo_id: int) -> bool:
        """Delete a todo."""
        with self._get_conn() as conn:
            cursor = conn.execute("DELETE FROM todos WHERE id = ?", (todo_id,))
            conn.commit()
            return cursor.rowcount > 0

    def start_timer(self, todo_id: int) -> Optional[Todo]:
        """Start time tracking for a todo."""
        todo = self.get(todo_id)
        if not todo:
            return None

        if todo.timer_started:
            return todo  # Already tracking

        todo.timer_started = datetime.now()
        todo.status = Status.DOING
        self.update(todo)
        return todo

    def stop_timer(self, todo_id: Optional[int] = None) -> Optional[Todo]:
        """Stop time tracking. If no ID, stops any active timer."""
        if todo_id:
            todo = self.get(todo_id)
            if todo and todo.timer_started:
                elapsed = datetime.now() - todo.timer_started
                todo.time_spent += elapsed
                todo.timer_started = None
                self.update(todo)
                return todo
        else:
            # Find and stop any active timer
            with self._get_conn() as conn:
                row = conn.execute(
                    "SELECT * FROM todos WHERE timer_started IS NOT NULL"
                ).fetchone()
                if row:
                    return self.stop_timer(row["id"])
        return None

    def mark_done(self, todo_id: int) -> Optional[Todo]:
        """Mark a todo as done."""
        todo = self.get(todo_id)
        if not todo:
            return None

        # Stop timer if running
        if todo.timer_started:
            elapsed = datetime.now() - todo.timer_started
            todo.time_spent += elapsed
            todo.timer_started = None

        todo.status = Status.DONE
        todo.completed_at = datetime.now()
        self.update(todo)
        return todo

    def get_active_timer(self) -> Optional[Todo]:
        """Get the currently tracking todo."""
        with self._get_conn() as conn:
            row = conn.execute(
                "SELECT * FROM todos WHERE timer_started IS NOT NULL"
            ).fetchone()
            return self._row_to_todo(row) if row else None

    def get_projects(self) -> list[str]:
        """Get list of all projects."""
        with self._get_conn() as conn:
            rows = conn.execute(
                "SELECT DISTINCT project FROM todos WHERE project IS NOT NULL ORDER BY project"
            ).fetchall()
            return [row["project"] for row in rows]

    def get_stats(self) -> dict:
        """Get todo statistics."""
        with self._get_conn() as conn:
            total = conn.execute("SELECT COUNT(*) FROM todos").fetchone()[0]
            done = conn.execute("SELECT COUNT(*) FROM todos WHERE status = 'done'").fetchone()[0]
            doing = conn.execute("SELECT COUNT(*) FROM todos WHERE status = 'doing'").fetchone()[0]
            todo = conn.execute("SELECT COUNT(*) FROM todos WHERE status = 'todo'").fetchone()[0]
            total_time = conn.execute("SELECT SUM(time_spent_seconds) FROM todos").fetchone()[0] or 0

            return {
                "total": total,
                "done": done,
                "doing": doing,
                "todo": todo,
                "total_time_seconds": total_time,
            }
