"""
Database Initialization Service.

This service handles programmatic creation of Appwrite database tables,
including schema definition, column creation, and index setup.
"""

import os
import time
from typing import List, Dict, Any, Optional

from appwrite.client import Client
from appwrite.services.tables_db import TablesDB
from appwrite.exception import AppwriteException

from app.utils.logging import get_logger

logger = get_logger(__file__)


class DatabaseInitService:
    """
    Service for initializing Appwrite database tables.

    This service provides methods to:
    - Create tables if they don't exist
    - Define table schemas (columns/attributes)
    - Create indexes for efficient querying
    - Validate existing table structures
    """

    def __init__(self):
        """
        Initialize the database initialization service.

        Reads configuration from environment variables:
        - APPWRITE_ENDPOINT: Appwrite API endpoint
        - APPWRITE_PROJECT_ID: Appwrite project ID
        - APPWRITE_API_KEY: Appwrite API key
        - APPWRITE_DATABASE_ID: Appwrite database ID
        """
        self.endpoint = os.getenv('APPWRITE_ENDPOINT')
        self.project_id = os.getenv('APPWRITE_PROJECT_ID')
        self.api_key = os.getenv('APPWRITE_API_KEY')
        self.database_id = os.getenv('APPWRITE_DATABASE_ID', 'main')

        if not all([self.endpoint, self.project_id, self.api_key]):
            logger.error("Missing Appwrite configuration in environment variables")
            raise ValueError("Appwrite configuration incomplete. Check APPWRITE_* environment variables.")

        # Initialize Appwrite client
        self.client = Client()
        self.client.set_endpoint(self.endpoint)
        self.client.set_project(self.project_id)
        self.client.set_key(self.api_key)

        # Initialize TablesDB service
        self.tables_db = TablesDB(self.client)

        logger.info("DatabaseInitService initialized", database_id=self.database_id)

    def init_all_tables(self) -> Dict[str, bool]:
        """
        Initialize all application tables.

        Returns:
            Dictionary mapping table names to success status
        """
        results = {}

        logger.info("Initializing all database tables")

        # Initialize characters table
        try:
            self.init_characters_table()
            results['characters'] = True
            logger.info("Characters table initialized successfully")
        except Exception as e:
            logger.error("Failed to initialize characters table", error=str(e))
            results['characters'] = False

        # Initialize game_sessions table
        try:
            self.init_game_sessions_table()
            results['game_sessions'] = True
            logger.info("Game sessions table initialized successfully")
        except Exception as e:
            logger.error("Failed to initialize game_sessions table", error=str(e))
            results['game_sessions'] = False

        # Initialize ai_usage_logs table
        try:
            self.init_ai_usage_logs_table()
            results['ai_usage_logs'] = True
            logger.info("AI usage logs table initialized successfully")
        except Exception as e:
            logger.error("Failed to initialize ai_usage_logs table", error=str(e))
            results['ai_usage_logs'] = False

        success_count = sum(1 for v in results.values() if v)
        total_count = len(results)

        logger.info("Table initialization complete",
                   success=success_count,
                   total=total_count,
                   results=results)

        return results

    def init_characters_table(self) -> bool:
        """
        Initialize the characters table.

        Table schema:
        - userId (string, required): Owner's user ID
        - characterData (string, required): JSON-serialized character data
        - is_active (boolean, default=True): Soft delete flag
        - created_at (datetime): Auto-managed creation timestamp
        - updated_at (datetime): Auto-managed update timestamp

        Indexes:
        - userId: For general user queries
        - userId + is_active: Composite index for efficiently fetching active characters

        Returns:
            True if successful

        Raises:
            AppwriteException: If table creation fails
        """
        table_id = 'characters'

        logger.info("Initializing characters table", table_id=table_id)

        try:
            # Check if table already exists
            try:
                self.tables_db.get_table(
                    database_id=self.database_id,
                    table_id=table_id
                )
                logger.info("Characters table already exists", table_id=table_id)
                return True
            except AppwriteException as e:
                if e.code != 404:
                    raise
                logger.info("Characters table does not exist, creating...")

            # Create table
            logger.info("Creating characters table")
            table = self.tables_db.create_table(
                database_id=self.database_id,
                table_id=table_id,
                name='Characters'
            )
            logger.info("Characters table created", table_id=table['$id'])

            # Create columns
            self._create_column(
                table_id=table_id,
                column_id='userId',
                column_type='string',
                size=255,
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='characterData',
                column_type='string',
                size=65535,  # Large text field for JSON data
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='is_active',
                column_type='boolean',
                required=False,  # Cannot be required if we want a default value
                default=True
            )

            # Note: created_at and updated_at are auto-managed by DatabaseService
            # through the _parse_row method and timestamp updates

            # Wait for columns to fully propagate in Appwrite before creating indexes
            logger.info("Waiting for columns to propagate before creating indexes...")
            time.sleep(2)

            # Create indexes for efficient querying
            # Note: Individual userId index for general user queries
            self._create_index(
                table_id=table_id,
                index_id='idx_userId',
                index_type='key',
                attributes=['userId']
            )

            # Composite index for the most common query pattern:
            # Query.equal('userId', user_id) + Query.equal('is_active', True)
            # This single composite index covers both conditions efficiently
            self._create_index(
                table_id=table_id,
                index_id='idx_userId_is_active',
                index_type='key',
                attributes=['userId', 'is_active']
            )

            logger.info("Characters table initialized successfully", table_id=table_id)
            return True

        except AppwriteException as e:
            logger.error("Failed to initialize characters table",
                        table_id=table_id,
                        error=str(e),
                        code=e.code)
            raise

    def init_game_sessions_table(self) -> bool:
        """
        Initialize the game_sessions table.

        Table schema:
        - userId (string, required): Owner's user ID
        - characterId (string, required): Character ID for this session
        - sessionData (string, required): JSON-serialized session data
        - status (string, required): Session status (active, completed, abandoned)
        - sessionType (string, required): Session type (solo, multiplayer)

        Indexes:
        - userId: For user session queries
        - userId + status: For active session queries
        - characterId: For character session lookups

        Returns:
            True if successful

        Raises:
            AppwriteException: If table creation fails
        """
        table_id = 'game_sessions'

        logger.info("Initializing game_sessions table", table_id=table_id)

        try:
            # Check if table already exists
            try:
                self.tables_db.get_table(
                    database_id=self.database_id,
                    table_id=table_id
                )
                logger.info("Game sessions table already exists", table_id=table_id)
                return True
            except AppwriteException as e:
                if e.code != 404:
                    raise
                logger.info("Game sessions table does not exist, creating...")

            # Create table
            logger.info("Creating game_sessions table")
            table = self.tables_db.create_table(
                database_id=self.database_id,
                table_id=table_id,
                name='Game Sessions'
            )
            logger.info("Game sessions table created", table_id=table['$id'])

            # Create columns
            self._create_column(
                table_id=table_id,
                column_id='userId',
                column_type='string',
                size=255,
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='characterId',
                column_type='string',
                size=255,
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='sessionData',
                column_type='string',
                size=65535,  # Large text field for JSON data
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='status',
                column_type='string',
                size=50,
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='sessionType',
                column_type='string',
                size=50,
                required=True
            )

            # Wait for columns to fully propagate
            logger.info("Waiting for columns to propagate before creating indexes...")
            time.sleep(2)

            # Create indexes
            self._create_index(
                table_id=table_id,
                index_id='idx_userId',
                index_type='key',
                attributes=['userId']
            )

            self._create_index(
                table_id=table_id,
                index_id='idx_userId_status',
                index_type='key',
                attributes=['userId', 'status']
            )

            self._create_index(
                table_id=table_id,
                index_id='idx_characterId',
                index_type='key',
                attributes=['characterId']
            )

            logger.info("Game sessions table initialized successfully", table_id=table_id)
            return True

        except AppwriteException as e:
            logger.error("Failed to initialize game_sessions table",
                        table_id=table_id,
                        error=str(e),
                        code=e.code)
            raise

    def init_ai_usage_logs_table(self) -> bool:
        """
        Initialize the ai_usage_logs table for tracking AI API usage and costs.

        Table schema:
        - user_id (string, required): User who made the request
        - timestamp (string, required): ISO timestamp of the request
        - model (string, required): Model identifier
        - tokens_input (integer, required): Input token count
        - tokens_output (integer, required): Output token count
        - tokens_total (integer, required): Total token count
        - estimated_cost (float, required): Estimated cost in USD
        - task_type (string, required): Type of task
        - session_id (string, optional): Game session ID
        - character_id (string, optional): Character ID
        - request_duration_ms (integer): Request duration in milliseconds
        - success (boolean): Whether request succeeded
        - error_message (string, optional): Error message if failed

        Indexes:
        - user_id: For user usage queries
        - timestamp: For date range queries
        - user_id + timestamp: Composite for user date range queries

        Returns:
            True if successful

        Raises:
            AppwriteException: If table creation fails
        """
        table_id = 'ai_usage_logs'

        logger.info("Initializing ai_usage_logs table", table_id=table_id)

        try:
            # Check if table already exists
            try:
                self.tables_db.get_table(
                    database_id=self.database_id,
                    table_id=table_id
                )
                logger.info("AI usage logs table already exists", table_id=table_id)
                return True
            except AppwriteException as e:
                if e.code != 404:
                    raise
                logger.info("AI usage logs table does not exist, creating...")

            # Create table
            logger.info("Creating ai_usage_logs table")
            table = self.tables_db.create_table(
                database_id=self.database_id,
                table_id=table_id,
                name='AI Usage Logs'
            )
            logger.info("AI usage logs table created", table_id=table['$id'])

            # Create columns
            self._create_column(
                table_id=table_id,
                column_id='user_id',
                column_type='string',
                size=255,
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='timestamp',
                column_type='string',
                size=50,  # ISO timestamp format
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='model',
                column_type='string',
                size=255,
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='tokens_input',
                column_type='integer',
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='tokens_output',
                column_type='integer',
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='tokens_total',
                column_type='integer',
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='estimated_cost',
                column_type='float',
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='task_type',
                column_type='string',
                size=50,
                required=True
            )

            self._create_column(
                table_id=table_id,
                column_id='session_id',
                column_type='string',
                size=255,
                required=False
            )

            self._create_column(
                table_id=table_id,
                column_id='character_id',
                column_type='string',
                size=255,
                required=False
            )

            self._create_column(
                table_id=table_id,
                column_id='request_duration_ms',
                column_type='integer',
                required=False,
                default=0
            )

            self._create_column(
                table_id=table_id,
                column_id='success',
                column_type='boolean',
                required=False,
                default=True
            )

            self._create_column(
                table_id=table_id,
                column_id='error_message',
                column_type='string',
                size=1000,
                required=False
            )

            # Wait for columns to fully propagate
            logger.info("Waiting for columns to propagate before creating indexes...")
            time.sleep(2)

            # Create indexes
            self._create_index(
                table_id=table_id,
                index_id='idx_user_id',
                index_type='key',
                attributes=['user_id']
            )

            self._create_index(
                table_id=table_id,
                index_id='idx_timestamp',
                index_type='key',
                attributes=['timestamp']
            )

            self._create_index(
                table_id=table_id,
                index_id='idx_user_id_timestamp',
                index_type='key',
                attributes=['user_id', 'timestamp']
            )

            logger.info("AI usage logs table initialized successfully", table_id=table_id)
            return True

        except AppwriteException as e:
            logger.error("Failed to initialize ai_usage_logs table",
                        table_id=table_id,
                        error=str(e),
                        code=e.code)
            raise

    def _create_column(
        self,
        table_id: str,
        column_id: str,
        column_type: str,
        size: Optional[int] = None,
        required: bool = False,
        default: Optional[Any] = None,
        array: bool = False
    ) -> Dict[str, Any]:
        """
        Create a column in a table.

        Args:
            table_id: Table ID
            column_id: Column ID
            column_type: Column type (string, integer, float, boolean, datetime, email, ip, url)
            size: Column size (for string types)
            required: Whether column is required
            default: Default value
            array: Whether column is an array

        Returns:
            Column creation response

        Raises:
            AppwriteException: If column creation fails
        """
        try:
            logger.info("Creating column",
                       table_id=table_id,
                       column_id=column_id,
                       column_type=column_type)

            # Build column parameters (Appwrite SDK uses 'key' not 'column_id')
            params = {
                'database_id': self.database_id,
                'table_id': table_id,
                'key': column_id,
                'required': required,
                'array': array
            }

            if size is not None:
                params['size'] = size

            if default is not None:
                params['default'] = default

            # Create column using the appropriate method based on type
            if column_type == 'string':
                result = self.tables_db.create_string_column(**params)
            elif column_type == 'integer':
                result = self.tables_db.create_integer_column(**params)
            elif column_type == 'float':
                result = self.tables_db.create_float_column(**params)
            elif column_type == 'boolean':
                result = self.tables_db.create_boolean_column(**params)
            elif column_type == 'datetime':
                result = self.tables_db.create_datetime_column(**params)
            elif column_type == 'email':
                result = self.tables_db.create_email_column(**params)
            else:
                raise ValueError(f"Unsupported column type: {column_type}")

            logger.info("Column created successfully",
                       table_id=table_id,
                       column_id=column_id)

            return result

        except AppwriteException as e:
            # If column already exists, log warning but don't fail
            if e.code == 409:  # Conflict - column already exists
                logger.warning("Column already exists",
                             table_id=table_id,
                             column_id=column_id)
                return {}
            logger.error("Failed to create column",
                        table_id=table_id,
                        column_id=column_id,
                        error=str(e),
                        code=e.code)
            raise

    def _create_index(
        self,
        table_id: str,
        index_id: str,
        index_type: str,
        attributes: List[str],
        orders: Optional[List[str]] = None
    ) -> Dict[str, Any]:
        """
        Create an index on a table.

        Args:
            table_id: Table ID
            index_id: Index ID
            index_type: Index type (key, fulltext, unique)
            attributes: List of column IDs to index
            orders: List of sort orders (ASC, DESC) for each attribute

        Returns:
            Index creation response

        Raises:
            AppwriteException: If index creation fails
        """
        try:
            logger.info("Creating index",
                       table_id=table_id,
                       index_id=index_id,
                       attributes=attributes)

            result = self.tables_db.create_index(
                database_id=self.database_id,
                table_id=table_id,
                key=index_id,
                type=index_type,
                columns=attributes,  # SDK uses 'columns', not 'attributes'
                orders=orders or ['ASC'] * len(attributes)
            )

            logger.info("Index created successfully",
                       table_id=table_id,
                       index_id=index_id)

            return result

        except AppwriteException as e:
            # If index already exists, log warning but don't fail
            if e.code == 409:  # Conflict - index already exists
                logger.warning("Index already exists",
                             table_id=table_id,
                             index_id=index_id)
                return {}
            logger.error("Failed to create index",
                        table_id=table_id,
                        index_id=index_id,
                        error=str(e),
                        code=e.code)
            raise


# Global instance for convenience
_init_service_instance: Optional[DatabaseInitService] = None


def get_database_init_service() -> DatabaseInitService:
    """
    Get the global DatabaseInitService instance.

    Returns:
        Singleton DatabaseInitService instance
    """
    global _init_service_instance
    if _init_service_instance is None:
        _init_service_instance = DatabaseInitService()
    return _init_service_instance


def init_database() -> Dict[str, bool]:
    """
    Convenience function to initialize all database tables.

    Returns:
        Dictionary mapping table names to success status
    """
    service = get_database_init_service()
    return service.init_all_tables()
