package main

import (
	"database/sql"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/charmbracelet/lipgloss"
	"github.com/evertras/bubble-table/table"
	_ "github.com/lib/pq"
)

type DBConfig struct {
	Host     string
	Port     string
	Database string
	User     string
	Password string
}

func getDBConfig(serviceName string) (*DBConfig, error) {
	configPath := os.ExpandEnv("$HOME/.pg_service.conf")
	data, err := os.ReadFile(configPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read ~/.pg_service.conf: %w", err)
	}

	lines := strings.Split(string(data), "\n")
	var currentService string
	config := &DBConfig{}

	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
			currentService = strings.Trim(line, "[]")
			if currentService == serviceName {
				config = &DBConfig{}
			}
			continue
		}

		if currentService == serviceName {
			parts := strings.SplitN(line, "=", 2)
			if len(parts) == 2 {
				key := strings.TrimSpace(parts[0])
				value := strings.TrimSpace(parts[1])
				switch key {
				case "host":
					config.Host = value
				case "port":
					config.Port = value
				case "dbname":
					config.Database = value
				case "user":
					config.User = value
				case "password":
					config.Password = value
				}
			}
		}
	}

	if config.Host == "" {
		return nil, fmt.Errorf("service '%s' not found in ~/.pg_service.conf", serviceName)
	}

	// Set defaults
	if config.Port == "" {
		config.Port = "5432"
	}

	return config, nil
}

func listServices() ([]string, error) {
	configPath := os.ExpandEnv("$HOME/.pg_service.conf")
	data, err := os.ReadFile(configPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read ~/.pg_service.conf: %w", err)
	}

	lines := strings.Split(string(data), "\n")
	var services []string

	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
			service := strings.Trim(line, "[]")
			services = append(services, service)
		}
	}

	return services, nil
}

func connectDB(serviceName string) (*sql.DB, error) {
	config, err := getDBConfig(serviceName)
	if err != nil {
		return nil, err
	}

	dsn := fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=require",
		config.Host, config.Port, config.Database, config.User, config.Password)

	db, err := sql.Open("postgres", dsn)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to database: %w", err)
	}

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

	return db, nil
}

func executeQuery(db *sql.DB, query string) (string, error) {
	rows, err := db.Query(query)
	if err != nil {
		return "", fmt.Errorf("failed to execute query: %w", err)
	}
	defer rows.Close()

	columns, err := rows.Columns()
	if err != nil {
		return "", fmt.Errorf("failed to get columns: %w", err)
	}

	// First pass: collect all data to calculate column widths
	var allRows [][]string
	values := make([]interface{}, len(columns))
	valuePtrs := make([]interface{}, len(columns))
	for i := range values {
		valuePtrs[i] = &values[i]
	}

	for rows.Next() {
		if err := rows.Scan(valuePtrs...); err != nil {
			return "", fmt.Errorf("failed to scan row: %w", err)
		}

		row := make([]string, len(columns))
		for i, val := range values {
			if val == nil {
				row[i] = "NULL"
			} else {
				// Handle byte arrays (convert to string)
				if bytes, ok := val.([]byte); ok {
					row[i] = strings.ReplaceAll(string(bytes), "\n", " ")
				} else {
					row[i] = strings.ReplaceAll(fmt.Sprintf("%v", val), "\n", " ")
				}
			}
		}
		allRows = append(allRows, row)
	}

	// Calculate optimal column widths
	colWidths := make([]int, len(columns))
	for i, col := range columns {
		colWidths[i] = len(col) + 2 // Start with header width + padding
	}

	for _, row := range allRows {
		for i, cell := range row {
			if len(cell)+2 > colWidths[i] {
				colWidths[i] = len(cell) + 2
			}
		}
	}

	// Create table columns with calculated widths
	var tableColumns []table.Column
	for i, col := range columns {
		// Limit maximum column width to prevent extremely wide columns
		maxWidth := colWidths[i]
		if maxWidth > 50 {
			maxWidth = 50
		}
		if maxWidth < 8 {
			maxWidth = 8
		}
		tableColumns = append(tableColumns, table.NewColumn(col, col, maxWidth).WithFiltered(true))
	}

	// Create table rows with the collected data
	var tableRows []table.Row
	for _, row := range allRows {
		rowData := table.RowData{}
		for i, cellValue := range row {
			rowData[columns[i]] = cellValue
		}
		tableRows = append(tableRows, table.NewRow(rowData))
	}

	if err := rows.Err(); err != nil {
		return "", fmt.Errorf("error iterating rows: %w", err)
	}

	// Define styles with nice colors
	baseStyle := lipgloss.NewStyle().
		BorderStyle(lipgloss.RoundedBorder()).
		BorderForeground(lipgloss.Color("#7C3AED")).
		Foreground(lipgloss.Color("#E5E7EB"))

	headerStyle := lipgloss.NewStyle().
		Foreground(lipgloss.Color("#FFFFFF")).
		Background(lipgloss.Color("#7C3AED")).
		Bold(true).
		Padding(0, 1)

	// Create and configure the bubble table with colors
	t := table.New(tableColumns).
		WithRows(tableRows).
		WithBaseStyle(baseStyle).
		HeaderStyle(headerStyle).
		WithMissingDataIndicator(lipgloss.NewStyle().
			Foreground(lipgloss.Color("#6B7280")).
			Italic(true).
			Render("NULL"))

	return t.View(), nil
}

func renderConnectionBarChart(db *sql.DB, query string, queryName string, model *Model) (string, error) {
	// Only render charts for the Home query
	if IsHomeTab(queryName) {
		// Calculate chart width for responsive rendering
		chartWidth := GetChartWidth(model.width)

		// Get the bar chart with responsive width
		barChart, err := RenderHomeChart(db, query, chartWidth)
		if err != nil {
			return "", err
		}

		// Update sparkline data with transaction commits
		currentCommits, err := GetTransactionCommits(db)
		if err != nil {
			// If we can't get commits, just show the bar chart
			return barChart, nil
		}

		// Calculate commits per second (rate of change)
		var commitsPerSec float64
		if model.lastCommits > 0 {
			commitsPerSec = currentCommits - model.lastCommits
		}
		model.lastCommits = currentCommits

		// Add data point to sparkline
		model.sparklineData.AddPoint(commitsPerSec, time.Now())

		// Render sparkline chart with responsive width
		sparklineChart := RenderSparklineChart(model.sparklineData, chartWidth)

		// Combine charts and table in vertical layout
		return RenderHomeSideBySide(barChart, sparklineChart, model.width), nil
	}
	return executeQuery(db, query)
}
