"""
Test utilities for database testing

Provides helpers for creating and managing test databases for unit tests.
"""

import sqlite3
import tempfile
from pathlib import Path
from typing import Optional

from evoseer_utils.db_connection import DbConnection


class DatabaseFixture:
    """
    Context manager for creating and managing test databases.

    Usage:
        with DatabaseFixture() as db:
            # Use db.path for database operations
            DbConnection.set_db_path(db.path)
            # Run tests...
        # Database is automatically cleaned up
    """

    def __init__(self, schema_file: Optional[str] = None):
        """
        Initialize test database context manager.

        Args:
            schema_file: Optional path to schema.sql file. If not provided,
                        uses the default schema from project root.
        """
        self.schema_file = schema_file
        self.temp_file = None
        self.path = None
        self._conn = None

    def __enter__(self):
        """Create temporary database and initialize schema"""
        # Create temporary database file
        self.temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".db", delete=False)
        self.path = self.temp_file.name
        self.temp_file.close()

        # Load schema
        if self.schema_file is None:
            # Use default schema from data directory
            self.schema_file = str(Path(__file__).parent / "data" / "schema.sql")

        self._init_schema()

        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Clean up temporary database"""
        # Close any active connections
        if self._conn is not None:
            self._conn.close()

        # Close DbConnection singleton
        DbConnection.close()

        # Remove temporary file
        if self.path and Path(self.path).exists():
            Path(self.path).unlink()

    def _init_schema(self):
        """Initialize database with schema"""
        conn = sqlite3.connect(self.path)
        cursor = conn.cursor()

        # Read and execute schema
        with open(self.schema_file) as f:
            schema = f.read()

        cursor.executescript(schema)
        conn.commit()
        conn.close()

    def get_connection(self) -> sqlite3.Connection:
        """
        Get a connection to the test database.

        Returns:
            sqlite3.Connection instance
        """
        if self._conn is None:
            self._conn = sqlite3.connect(self.path)
            self._conn.row_factory = sqlite3.Row

        return self._conn

    def add_test_gene(
        self,
        gene_id: str,
        gene_name: str,
        chrom: int,
        start: int,
        end: int,
        strand: str = "+",
        tss: Optional[int] = None,
    ) -> int:
        """
        Add a test gene to the database.

        Args:
            gene_id: Ensembl gene ID (e.g., ENSG00000141510)
            gene_name: Gene symbol (e.g., TP53)
            chrom: Chromosome (1-24)
            start: Gene start position
            end: Gene end position
            strand: Strand (+ or -)
            tss: Transcription start site (defaults to start)

        Returns:
            Database ID of inserted gene
        """
        if tss is None:
            tss = start

        conn = self.get_connection()
        cursor = conn.cursor()

        cursor.execute(
            """
            INSERT INTO genes (gene_id, gene_name, chrom, start, end, strand, tss)
            VALUES (?, ?, ?, ?, ?, ?, ?)
        """,
            (gene_id, gene_name, chrom, start, end, strand, tss),
        )

        conn.commit()
        return cursor.lastrowid

    def add_test_feature(
        self,
        gene_id: int,
        annotation_type: str,
        feature_start: int,
        feature_end: int,
        transcript_id: Optional[str] = None,
    ) -> int:
        """
        Add a test genomic feature to the database.

        Args:
            gene_id: Database ID of the gene
            annotation_type: Type (exon, intron, UTR5, UTR3, promoter)
            feature_start: Feature start position
            feature_end: Feature end position
            transcript_id: Optional transcript ID

        Returns:
            Database ID of inserted feature
        """
        conn = self.get_connection()
        cursor = conn.cursor()

        cursor.execute(
            """
            INSERT INTO genomic_features
            (gene_id, annotation_type, feature_start, feature_end, transcript_id)
            VALUES (?, ?, ?, ?, ?)
        """,
            (gene_id, annotation_type, feature_start, feature_end, transcript_id),
        )

        conn.commit()
        return cursor.lastrowid

    def add_test_mutation(self, chrom: int, pos: int, ref: str, alt: str) -> int:
        """
        Add a test mutation to the database.

        Args:
            chrom: Chromosome (1-24)
            pos: Genomic position
            ref: Reference allele
            alt: Alternate allele

        Returns:
            Database ID of inserted mutation
        """
        conn = self.get_connection()
        cursor = conn.cursor()

        cursor.execute(
            """
            INSERT INTO mutations (chrom, pos, ref, alt)
            VALUES (?, ?, ?, ?)
        """,
            (chrom, pos, ref, alt),
        )

        conn.commit()
        return cursor.lastrowid


class DatabaseFixtureWithData(DatabaseFixture):
    """DatabaseFixture that pre-populates with sample data"""

    def __enter__(self):
        """Create temporary database, initialize schema, and add sample data"""
        super().__enter__()

        # Add sample TP53 gene
        tp53_id = self.add_test_gene(
            gene_id="ENSG00000141510",
            gene_name="TP53",
            chrom=17,
            start=7661779,
            end=7687550,
            strand="-",
            tss=7687550,
        )

        # Add sample exon with canonical transcript
        self.add_test_feature(
            gene_id=tp53_id,
            annotation_type="exon",
            feature_start=7577100,
            feature_end=7577600,
            transcript_id="ENST00000269305",
        )

        # Add sample KRAS gene
        kras_id = self.add_test_gene(
            gene_id="ENSG00000133703",
            gene_name="KRAS",
            chrom=12,
            start=25205246,
            end=25250936,
            strand="-",
            tss=25250936,
        )

        # Add sample exon
        self.add_test_feature(
            gene_id=kras_id,
            annotation_type="exon",
            feature_start=25227000,
            feature_end=25227500,
            transcript_id="ENST00000311936",
        )

        return self


def create_test_db(with_sample_data: bool = False) -> DatabaseFixture:
    """
    Factory function to create a test database.

    Args:
        with_sample_data: If True, populate with sample genes and features

    Returns:
        DatabaseFixture context manager

    Usage:
        with create_test_db(with_sample_data=True) as db:
            DbConnection.set_db_path(db.path)
            # Run tests...
    """
    if with_sample_data:
        return DatabaseFixtureWithData()
    else:
        return DatabaseFixture()
