"""
MySQL Connector

Connects to MySQL databases for query execution.
"""

from __future__ import annotations

from typing import Any

from ..exceptions import ConnectionError, ValidationError
from .base import BaseConnector, ConnectorRelationship, ConnectorTable, ConnectorTableColumn

# Type mapping from MySQL to SDK types
MYSQL_TYPE_MAP = {
    # Integer types
    "tinyint": "integer",
    "smallint": "integer",
    "mediumint": "integer",
    "int": "integer",
    "integer": "integer",
    "bigint": "integer",
    "year": "integer",
    # Float types
    "float": "float",
    "double": "float",
    "real": "float",
    # Decimal types
    "decimal": "decimal",
    "numeric": "decimal",
    "dec": "decimal",
    "fixed": "decimal",
    # Boolean types
    "bool": "boolean",
    "boolean": "boolean",
    "bit": "boolean",
    # String types
    "char": "string",
    "varchar": "string",
    "tinytext": "string",
    "text": "string",
    "mediumtext": "string",
    "longtext": "string",
    "enum": "string",
    "set": "string",
    "json": "string",
    # Date/time types
    "date": "date",
    "time": "time",
    "datetime": "timestamp",
    "timestamp": "timestamp",
    # Binary types
    "binary": "binary",
    "varbinary": "binary",
    "tinyblob": "binary",
    "blob": "binary",
    "mediumblob": "binary",
    "longblob": "binary",
}


class MySQLConnector(BaseConnector):
    """MySQL database connector.

    Connects to a MySQL database using mysql-connector-python. Supports both
    connection string and individual parameters.

    Args:
        connection_string: MySQL connection string (mysql://user:pass@host:port/db)
        host: Database host
        port: Database port (default: 3306)
        database: Database name
        user: Database user
        password: Database password
        tables: List of table configurations

    Example:
        ```python
        connector = MySQLConnector(
            host="localhost",
            port=3306,
            database="analytics",
            user="root",
            password="secret",
            tables=[
                ConnectorTable(
                    name="orders",
                    description="Customer orders",
                    columns=[
                        ConnectorTableColumn(name="id", type="integer"),
                        ConnectorTableColumn(name="amount", type="decimal"),
                    ],
                ),
            ],
        )
        ```
    """

    def __init__(
        self,
        connection_string: str | None = None,
        host: str | None = None,
        port: int = 3306,
        database: str | None = None,
        user: str | None = None,
        password: str | None = None,
        tables: list[ConnectorTable] | None = None,
        relationships: list[ConnectorRelationship] | None = None,
    ):
        super().__init__(tables=tables, relationships=relationships)

        self._connection_string = connection_string
        self._host = host
        self._port = port
        self._database = database
        self._user = user
        self._password = password
        self._conn = None

        if not connection_string and not (host and database):
            raise ValidationError(
                "Either connection_string or host+database must be provided",
                field="connection",
            )

        # Parse connection string to extract database name for discover queries
        if connection_string and not database:
            self._database = self._parse_database_from_url(connection_string)

    @property
    def dialect(self) -> str:
        return "mysql"

    def connect(self) -> None:
        """Establish connection to MySQL."""
        try:
            import mysql.connector
        except ImportError as e:
            raise ConnectionError(
                "mysql-connector-python is required for MySQLConnector. "
                "Install it with: pip install mysql-connector-python",
                original_error=e,
            )

        try:
            if self._connection_string:
                # Parse the connection string into components
                params = self._parse_connection_string(self._connection_string)
                self._conn = mysql.connector.connect(**params)
            else:
                connect_params = {
                    "host": self._host,
                    "port": self._port,
                    "database": self._database,
                    "user": self._user,
                }
                if self._password is not None:
                    connect_params["password"] = self._password

                self._conn = mysql.connector.connect(**connect_params)
            self._connected = True
        except Exception as e:
            raise ConnectionError(f"Failed to connect to MySQL: {e}", original_error=e)

    def disconnect(self) -> None:
        """Close the MySQL connection."""
        if self._conn:
            try:
                self._conn.close()
            except Exception:
                pass
        self._conn = None
        self._connected = False

    def execute(self, sql: str) -> list[dict[str, Any]]:
        """Execute SQL against MySQL and return results."""
        if not self._conn:
            self.connect()

        try:
            cursor = self._conn.cursor(dictionary=True)
            cursor.execute(sql)

            if cursor.description is None:
                return []

            rows = cursor.fetchall()

            return [
                {col: self._convert_value(val) for col, val in row.items()}
                for row in rows
            ]
        except Exception as e:
            raise ConnectionError(f"Query execution failed: {e}", original_error=e)

    def discover_tables(self) -> list[str]:
        """List all tables in the current database."""
        if not self._conn:
            self.connect()

        db = self._database or ""
        result = self.execute(
            f"SELECT table_name FROM information_schema.tables "
            f"WHERE table_schema = '{db}' "
            f"ORDER BY table_name"
        )
        return [row.get("table_name") or row.get("TABLE_NAME") for row in result]

    def discover_columns(self, table_name: str) -> list[ConnectorTableColumn]:
        """Discover columns for a table."""
        if not self._conn:
            self.connect()

        db = self._database or ""
        result = self.execute(
            f"SELECT column_name, data_type "
            f"FROM information_schema.columns "
            f"WHERE table_schema = '{db}' "
            f"AND table_name = '{table_name}' "
            f"ORDER BY ordinal_position"
        )

        return [
            ConnectorTableColumn(
                name=row.get("column_name") or row.get("COLUMN_NAME"),
                type=MYSQL_TYPE_MAP.get(
                    (row.get("data_type") or row.get("DATA_TYPE") or "").lower(),
                    row.get("data_type") or row.get("DATA_TYPE"),
                ),
            )
            for row in result
        ]

    @staticmethod
    def _convert_value(val: Any) -> Any:
        """Convert database values to JSON-serializable types."""
        if val is None:
            return None
        # Handle Decimal
        if hasattr(val, "as_tuple"):
            return float(val)
        # Handle datetime, date, time
        if hasattr(val, "isoformat"):
            return val.isoformat()
        # Handle timedelta (MySQL TIME can return timedelta)
        if hasattr(val, "total_seconds"):
            total = int(val.total_seconds())
            hours, remainder = divmod(abs(total), 3600)
            minutes, seconds = divmod(remainder, 60)
            sign = "-" if total < 0 else ""
            return f"{sign}{hours:02d}:{minutes:02d}:{seconds:02d}"
        # Handle bytes/bytearray
        if isinstance(val, (bytes, bytearray)):
            return str(val)
        return val

    @staticmethod
    def _parse_connection_string(connection_string: str) -> dict[str, Any]:
        """Parse a MySQL connection string into connect() parameters.

        Supports formats:
        - mysql://user:pass@host:port/database
        - mysql+mysqlconnector://user:pass@host:port/database
        """
        from urllib.parse import urlparse

        parsed = urlparse(connection_string)

        params: dict[str, Any] = {}

        if parsed.hostname:
            params["host"] = parsed.hostname
        if parsed.port:
            params["port"] = parsed.port
        if parsed.username:
            params["user"] = parsed.username
        if parsed.password:
            params["password"] = parsed.password
        if parsed.path and parsed.path != "/":
            params["database"] = parsed.path.lstrip("/")

        return params

    @staticmethod
    def _parse_database_from_url(connection_string: str) -> str | None:
        """Extract the database name from a connection string."""
        from urllib.parse import urlparse

        parsed = urlparse(connection_string)
        if parsed.path and parsed.path != "/":
            return parsed.path.lstrip("/")
        return None
