package services

import (
	"context"
	"fmt"
	"time"

	"github.com/ClickHouse/clickhouse-go/v2"
	"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
	"github.com/fiskerinc/cloud-services/pkg/logger"
	"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
)

var (
	// Local clickhouse for storing cost data
	localConn driver.Conn
	// Remote clickhouse (dev-cluster) for reading vehicle data
	remoteConn driver.Conn
)

func InitClickhouse() {
	// Local clickhouse (mini cluster) - for storing cost data
	localHost := envtool.GetEnv("CLICKHOUSE_HOST", "localhost")
	localPort := envtool.GetEnv("CLICKHOUSE_PORT", "9000")
	localUser := envtool.GetEnv("CLICKHOUSE_USER", "default")
	localPass := envtool.GetEnv("CLICKHOUSE_PASS", "")
	localDB := envtool.GetEnv("CLICKHOUSE_DB", "default")

	local, err := clickhouse.Open(&clickhouse.Options{
		Addr: []string{fmt.Sprintf("%s:%s", localHost, localPort)},
		Auth: clickhouse.Auth{
			Database: localDB,
			Username: localUser,
			Password: localPass,
		},
	})
	if err != nil {
		logger.Error().Err(err).Msg("Failed to connect to local ClickHouse")
	} else {
		localConn = local
		logger.Info().Msg("Local ClickHouse connection initialized")
	}

	// Remote clickhouse (dev-cluster) - for reading vehicle metrics
	remoteHost := envtool.GetEnv("REMOTE_CLICKHOUSE_HOST", "")
	remotePort := envtool.GetEnv("REMOTE_CLICKHOUSE_PORT", "9000")
	remoteUser := envtool.GetEnv("REMOTE_CLICKHOUSE_USER", "default")
	remotePass := envtool.GetEnv("REMOTE_CLICKHOUSE_PASS", "")
	remoteDB := envtool.GetEnv("REMOTE_CLICKHOUSE_DB", "default")

	if remoteHost != "" {
		remote, err := clickhouse.Open(&clickhouse.Options{
			Addr: []string{fmt.Sprintf("%s:%s", remoteHost, remotePort)},
			Auth: clickhouse.Auth{
				Database: remoteDB,
				Username: remoteUser,
				Password: remotePass,
			},
		})
		if err != nil {
			logger.Error().Err(err).Msg("Failed to connect to remote ClickHouse")
		} else {
			remoteConn = remote
			logger.Info().Msg("Remote ClickHouse connection initialized")
		}
	}

	// Create tables in local clickhouse
	if localConn != nil {
		if err := createTables(); err != nil {
			logger.Error().Err(err).Msg("Failed to create tables")
		}
	}
}

func createTables() error {
	ctx := context.Background()

	// VIN cost metrics table
	createSQL := `
		CREATE TABLE IF NOT EXISTS vin_cost_metrics (
			vin String,
			timestamp DateTime,
			cpu_cores Float64,
			memory_gb Float64,
			cloud_cost_usd Float64,
			onprem_cost_usd Float64,
			pod_count UInt32
		) ENGINE = MergeTree()
		ORDER BY (vin, timestamp)
		TTL timestamp + INTERVAL 90 DAY
	`
	if err := localConn.Exec(ctx, createSQL); err != nil {
		return fmt.Errorf("failed to create vin_cost_metrics table: %w", err)
	}

	// Hourly aggregates
	createAggSQL := `
		CREATE TABLE IF NOT EXISTS vin_cost_hourly (
			vin String,
			hour DateTime,
			avg_cpu_cores Float64,
			avg_memory_gb Float64,
			total_cloud_cost_usd Float64,
			total_onprem_cost_usd Float64,
			sample_count UInt32
		) ENGINE = SummingMergeTree()
		ORDER BY (vin, hour)
		TTL hour + INTERVAL 365 DAY
	`
	if err := localConn.Exec(ctx, createAggSQL); err != nil {
		return fmt.Errorf("failed to create vin_cost_hourly table: %w", err)
	}

	return nil
}

// CostRecord represents a cost entry for a VIN
type CostRecord struct {
	VIN            string
	Timestamp      time.Time
	CPUCores       float64
	MemoryGB       float64
	CloudCostUSD   float64
	OnpremCostUSD  float64
	PodCount       uint32
}

// InsertCostRecords batch inserts cost records to local clickhouse
func InsertCostRecords(records []CostRecord) error {
	if localConn == nil {
		return fmt.Errorf("local clickhouse not connected")
	}

	ctx := context.Background()
	batch, err := localConn.PrepareBatch(ctx, "INSERT INTO vin_cost_metrics")
	if err != nil {
		return err
	}

	for _, r := range records {
		if err := batch.Append(r.VIN, r.Timestamp, r.CPUCores, r.MemoryGB, r.CloudCostUSD, r.OnpremCostUSD, r.PodCount); err != nil {
			return err
		}
	}

	return batch.Send()
}

// VinActivity holds activity data from remote clickhouse
type VinActivity struct {
	VIN         string
	MessageCount uint64
	LastSeen    time.Time
}

// FetchActiveVins queries remote clickhouse for active VINs
func FetchActiveVins(from, to time.Time) ([]VinActivity, error) {
	if remoteConn == nil {
		return nil, fmt.Errorf("remote clickhouse not connected")
	}

	ctx := context.Background()
	
	// Try feature_table_last_shard first (small, ~46MB) then feature_table_temp_shard (dev-cluster)
	// Avoid feature_table_shard - it's 994GB and would kill the DB
	tables := []string{"feature_table_last_shard", "feature_table_temp_shard"}
	
	for _, table := range tables {
		query := fmt.Sprintf(`
			SELECT 
				VIN,
				count() as msg_count,
				max(Timestamp) as last_seen
			FROM %s
			WHERE Timestamp BETWEEN ? AND ?
			GROUP BY VIN
		`, table)

		rows, err := remoteConn.Query(ctx, query, from, to)
		if err != nil {
			logger.Debug().Err(err).Str("table", table).Msg("Table not found, trying next")
			continue
		}
		defer rows.Close()

		var result []VinActivity
		for rows.Next() {
			var v VinActivity
			if err := rows.Scan(&v.VIN, &v.MessageCount, &v.LastSeen); err != nil {
				continue
			}
			result = append(result, v)
		}
		
		logger.Debug().Str("table", table).Int("count", len(result)).Msg("Fetched active VINs")
		return result, nil
	}

	return nil, fmt.Errorf("no suitable feature table found")
}

// VinCostSummary holds aggregated cost data for a VIN
type VinCostSummary struct {
	VIN              string    `json:"vin"`
	StartTime        time.Time `json:"start_time"`
	EndTime          time.Time `json:"end_time"`
	TotalCloudCost   float64   `json:"total_cloud_cost_usd"`
	TotalOnpremCost  float64   `json:"total_onprem_cost_usd"`
	AvgCPUCores      float64   `json:"avg_cpu_cores"`
	AvgMemoryGB      float64   `json:"avg_memory_gb"`
	SampleCount      uint64    `json:"sample_count"`
	CostSavings      float64   `json:"cost_savings_usd"`
	SavingsPercent   float64   `json:"savings_percent"`
}

// GetVinCostSummary retrieves cost summary for a VIN from local clickhouse
func GetVinCostSummary(vin string, from, to time.Time) (*VinCostSummary, error) {
	if localConn == nil {
		return nil, fmt.Errorf("local clickhouse not connected")
	}

	ctx := context.Background()
	query := `
		SELECT 
			vin,
			min(timestamp) as start_time,
			max(timestamp) as end_time,
			sum(cloud_cost_usd) as total_cloud_cost,
			sum(onprem_cost_usd) as total_onprem_cost,
			avg(cpu_cores) as avg_cpu,
			avg(memory_gb) as avg_memory,
			count() as samples
		FROM vin_cost_metrics
		WHERE vin = ? AND timestamp BETWEEN ? AND ?
		GROUP BY vin
	`

	var summary VinCostSummary
	row := localConn.QueryRow(ctx, query, vin, from, to)
	if err := row.Scan(
		&summary.VIN,
		&summary.StartTime,
		&summary.EndTime,
		&summary.TotalCloudCost,
		&summary.TotalOnpremCost,
		&summary.AvgCPUCores,
		&summary.AvgMemoryGB,
		&summary.SampleCount,
	); err != nil {
		return nil, err
	}

	summary.CostSavings = summary.TotalCloudCost - summary.TotalOnpremCost
	if summary.TotalCloudCost > 0 {
		summary.SavingsPercent = (summary.CostSavings / summary.TotalCloudCost) * 100
	}

	return &summary, nil
}

// FleetCostSummary holds fleet-wide cost data
type FleetCostSummary struct {
	StartTime        time.Time         `json:"start_time"`
	EndTime          time.Time         `json:"end_time"`
	TotalCloudCost   float64           `json:"total_cloud_cost_usd"`
	TotalOnpremCost  float64           `json:"total_onprem_cost_usd"`
	TotalSavings     float64           `json:"total_savings_usd"`
	SavingsPercent   float64           `json:"savings_percent"`
	VehicleCount     uint64            `json:"vehicle_count"`
	TopCostVins      []VinCostSummary  `json:"top_cost_vins"`
}

// GetFleetCostSummary retrieves fleet-wide cost summary from local clickhouse
func GetFleetCostSummary(from, to time.Time, limit int) (*FleetCostSummary, error) {
	if localConn == nil {
		return nil, fmt.Errorf("local clickhouse not connected")
	}

	ctx := context.Background()

	// Get totals
	totalQuery := `
		SELECT 
			sum(cloud_cost_usd) as total_cloud,
			sum(onprem_cost_usd) as total_onprem,
			count(DISTINCT vin) as vehicle_count
		FROM vin_cost_metrics
		WHERE timestamp BETWEEN ? AND ?
	`

	var summary FleetCostSummary
	summary.StartTime = from
	summary.EndTime = to

	row := localConn.QueryRow(ctx, totalQuery, from, to)
	if err := row.Scan(&summary.TotalCloudCost, &summary.TotalOnpremCost, &summary.VehicleCount); err != nil {
		return nil, err
	}

	summary.TotalSavings = summary.TotalCloudCost - summary.TotalOnpremCost
	if summary.TotalCloudCost > 0 {
		summary.SavingsPercent = (summary.TotalSavings / summary.TotalCloudCost) * 100
	}

	// Get top cost VINs
	topQuery := `
		SELECT 
			vin,
			min(timestamp),
			max(timestamp),
			sum(cloud_cost_usd),
			sum(onprem_cost_usd),
			avg(cpu_cores),
			avg(memory_gb),
			count()
		FROM vin_cost_metrics
		WHERE timestamp BETWEEN ? AND ?
		GROUP BY vin
		ORDER BY sum(cloud_cost_usd) DESC
		LIMIT ?
	`

	rows, err := localConn.Query(ctx, topQuery, from, to, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var v VinCostSummary
		if err := rows.Scan(
			&v.VIN, &v.StartTime, &v.EndTime,
			&v.TotalCloudCost, &v.TotalOnpremCost,
			&v.AvgCPUCores, &v.AvgMemoryGB, &v.SampleCount,
		); err != nil {
			continue
		}
		v.CostSavings = v.TotalCloudCost - v.TotalOnpremCost
		if v.TotalCloudCost > 0 {
			v.SavingsPercent = (v.CostSavings / v.TotalCloudCost) * 100
		}
		summary.TopCostVins = append(summary.TopCostVins, v)
	}

	return &summary, nil
}
