package db

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"

	"github.com/insertr/insertr/internal/db/postgresql"
	"github.com/insertr/insertr/internal/db/sqlite"
)

// Database wraps the database connection and queries
type Database struct {
	conn   *sql.DB
	dbType string

	// Type-specific query interfaces
	sqliteQueries     *sqlite.Queries
	postgresqlQueries *postgresql.Queries
}

// NewDatabase creates a new database connection
func NewDatabase(dbPath string) (*Database, error) {
	var conn *sql.DB
	var dbType string
	var err error

	// Determine database type from connection string
	if strings.Contains(dbPath, "postgres://") || strings.Contains(dbPath, "postgresql://") {
		dbType = "postgresql"
		conn, err = sql.Open("postgres", dbPath)
	} else {
		dbType = "sqlite3"
		conn, err = sql.Open("sqlite3", dbPath)
	}

	if err != nil {
		return nil, fmt.Errorf("failed to open database: %w", err)
	}

	// Test connection
	if err := conn.Ping(); err != nil {
		conn.Close()
		return nil, fmt.Errorf("failed to ping database: %w", err)
	}

	// Initialize the appropriate queries
	db := &Database{
		conn:   conn,
		dbType: dbType,
	}

	switch dbType {
	case "sqlite3":
		// Initialize SQLite schema using generated functions
		db.sqliteQueries = sqlite.New(conn)
		if err := db.initializeSQLiteSchema(); err != nil {
			conn.Close()
			return nil, fmt.Errorf("failed to initialize SQLite schema: %w", err)
		}
	case "postgresql":
		// Initialize PostgreSQL schema using generated functions
		db.postgresqlQueries = postgresql.New(conn)
		if err := db.initializePostgreSQLSchema(); err != nil {
			conn.Close()
			return nil, fmt.Errorf("failed to initialize PostgreSQL schema: %w", err)
		}
	default:
		return nil, fmt.Errorf("unsupported database type: %s", dbType)
	}

	return db, nil
}

// Close closes the database connection
func (db *Database) Close() error {
	return db.conn.Close()
}

// GetQueries returns the appropriate query interface
func (db *Database) GetSQLiteQueries() *sqlite.Queries {
	return db.sqliteQueries
}

func (db *Database) GetPostgreSQLQueries() *postgresql.Queries {
	return db.postgresqlQueries
}

// GetDBType returns the database type
func (db *Database) GetDBType() string {
	return db.dbType
}

// initializeSQLiteSchema sets up the SQLite database schema
func (db *Database) initializeSQLiteSchema() error {
	ctx := context.Background()

	// Create tables
	if err := db.sqliteQueries.InitializeSchema(ctx); err != nil {
		return fmt.Errorf("failed to create content table: %w", err)
	}

	if err := db.sqliteQueries.InitializeVersionsTable(ctx); err != nil {
		return fmt.Errorf("failed to create content_versions table: %w", err)
	}

	// Create indexes manually (sqlc doesn't generate CREATE INDEX functions for SQLite)
	indexQueries := []string{
		"CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);",
		"CREATE INDEX IF NOT EXISTS idx_content_updated_at ON content(updated_at);",
		"CREATE INDEX IF NOT EXISTS idx_content_versions_lookup ON content_versions(content_id, site_id, created_at DESC);",
	}

	for _, query := range indexQueries {
		if _, err := db.conn.Exec(query); err != nil {
			return fmt.Errorf("failed to create index: %w", err)
		}
	}

	// Create update trigger manually (sqlc doesn't generate trigger creation functions)
	triggerQuery := `
	CREATE TRIGGER IF NOT EXISTS update_content_updated_at
	AFTER UPDATE ON content
	FOR EACH ROW
	BEGIN
		UPDATE content SET updated_at = strftime('%s', 'now') WHERE id = NEW.id AND site_id = NEW.site_id;
	END;`

	if _, err := db.conn.Exec(triggerQuery); err != nil {
		return fmt.Errorf("failed to create update trigger: %w", err)
	}

	return nil
}

// initializePostgreSQLSchema sets up the PostgreSQL database schema
func (db *Database) initializePostgreSQLSchema() error {
	ctx := context.Background()

	// Create tables using sqlc-generated functions
	if err := db.postgresqlQueries.InitializeSchema(ctx); err != nil {
		return fmt.Errorf("failed to create content table: %w", err)
	}

	if err := db.postgresqlQueries.InitializeVersionsTable(ctx); err != nil {
		return fmt.Errorf("failed to create content_versions table: %w", err)
	}

	// Create indexes using sqlc-generated functions (PostgreSQL supports this)
	if err := db.postgresqlQueries.CreateContentSiteIndex(ctx); err != nil {
		return fmt.Errorf("failed to create content site index: %w", err)
	}

	if err := db.postgresqlQueries.CreateContentUpdatedAtIndex(ctx); err != nil {
		return fmt.Errorf("failed to create content updated_at index: %w", err)
	}

	if err := db.postgresqlQueries.CreateVersionsLookupIndex(ctx); err != nil {
		return fmt.Errorf("failed to create versions lookup index: %w", err)
	}

	// Create update function using sqlc-generated function
	if err := db.postgresqlQueries.CreateUpdateFunction(ctx); err != nil {
		return fmt.Errorf("failed to create update function: %w", err)
	}

	// Create trigger manually (sqlc doesn't generate trigger creation functions)
	triggerQuery := `
		DROP TRIGGER IF EXISTS update_content_updated_at ON content;
		CREATE TRIGGER update_content_updated_at
		BEFORE UPDATE ON content
		FOR EACH ROW
		EXECUTE FUNCTION update_content_timestamp();`

	if _, err := db.conn.Exec(triggerQuery); err != nil {
		return fmt.Errorf("failed to create update trigger: %w", err)
	}

	return nil
}
