package database

import (
	"fmt"
	"strings"
	"time"

	"gorm.io/gorm"
)

const workflowAutomationReconciliation = "workflow_to_automation_v1"

type workflowAutomationTable struct {
	version    string
	workflow   string
	automation string
}

var workflowAutomationTables = []workflowAutomationTable{
	{"0001", "workflow_runs", "automation_runs"},
	{"0001", "workflow_job_runs", "automation_job_runs"},
	{"0001", "workflow_events", "automation_events"},
	{"0001", "workflow_jobs", "automation_jobs"},
	{"0001", "workflow_waits", "automation_waits"},
	{"0001", "workflow_runners", "automation_runners"},
	{"0001", "workflow_runner_tokens", "automation_runner_tokens"},
	{"0001", "workflow_runner_sessions", "automation_runner_sessions"},
	{"0001", "workflow_log_chunks", "automation_log_chunks"},
	{"0001", "workflow_artifacts", "automation_artifacts"},
	{"0007", "workflow_run_slots", "automation_run_slots"},
	{"0008", "workflow_pinned_repos", "automation_pinned_repos"},
	{"0010", "workflow_actions_runner_credentials", "automation_actions_runner_credentials"},
	{"0016", "workflow_secrets", "automation_secrets"},
	{"0017", "workflow_repo_status_outbox", "automation_repo_status_outbox"},
}

func reconcileWorkflowAutomationSchema(db *gorm.DB, applied map[string]bool) error {
	legacyDetected, err := validateWorkflowAutomationMigrationState(db, applied)
	if err != nil {
		return err
	}
	if !legacyDetected {
		return nil
	}

	for _, table := range workflowAutomationTables {
		if !applied[table.version] {
			continue
		}
		if !db.Migrator().HasTable(table.workflow) {
			continue
		}
		if err := db.Migrator().RenameTable(table.workflow, table.automation); err != nil {
			return fmt.Errorf("reconcile meshx workflow schema: rename table %s to %s: %w", table.workflow, table.automation, err)
		}
	}

	for _, columns := range [][2]string{{"workflow_path", "definition_path"}, {"workflow_id", "definition_id"}} {
		if !db.Migrator().HasColumn("automation_runs", columns[0]) {
			continue
		}
		if err := db.Migrator().RenameColumn("automation_runs", columns[0], columns[1]); err != nil {
			return fmt.Errorf("reconcile meshx workflow schema: rename automation_runs.%s to %s: %w", columns[0], columns[1], err)
		}
	}

	for _, table := range workflowAutomationTables {
		if !applied[table.version] {
			continue
		}
		indexes, err := db.Migrator().GetIndexes(table.automation)
		if err != nil {
			return fmt.Errorf("reconcile meshx workflow schema: list indexes for %s: %w", table.automation, err)
		}
		for _, index := range indexes {
			oldName := index.Name()
			if !strings.Contains(oldName, "workflow") {
				continue
			}
			newName := strings.ReplaceAll(oldName, "workflow", "automation")
			if db.Migrator().HasIndex(table.automation, newName) {
				return fmt.Errorf("reconcile meshx workflow schema: %s has both indexes %s and %s", table.automation, oldName, newName)
			}
			if err := db.Migrator().RenameIndex(table.automation, oldName, newName); err != nil {
				return fmt.Errorf("reconcile meshx workflow schema: rename index %s.%s to %s: %w", table.automation, oldName, newName, err)
			}
		}
	}

	if err := ensureSchemaReconciliationsTable(db); err != nil {
		return err
	}
	if err := db.Exec(
		"INSERT INTO schema_reconciliations (name, applied_at) VALUES (?, ?)",
		workflowAutomationReconciliation,
		time.Now().UTC(),
	).Error; err != nil {
		return fmt.Errorf("record meshx schema reconciliation marker %s: %w", workflowAutomationReconciliation, err)
	}
	return nil
}

func validateWorkflowAutomationMigrationState(db *gorm.DB, applied map[string]bool) (bool, error) {
	if !applied["0001"] {
		for _, table := range workflowAutomationTables {
			if db.Migrator().HasTable(table.workflow) {
				return false, fmt.Errorf("reconcile meshx workflow schema: table %s exists without applied migration 0001", table.workflow)
			}
		}
		return false, nil
	}

	legacyDetected := false
	for _, table := range workflowAutomationTables {
		workflowExists := db.Migrator().HasTable(table.workflow)
		automationExists := db.Migrator().HasTable(table.automation)
		if workflowExists && automationExists {
			return false, fmt.Errorf("reconcile meshx workflow schema: both %s and %s exist; refusing to overwrite either table", table.workflow, table.automation)
		}
		if !applied[table.version] {
			if workflowExists {
				return false, fmt.Errorf("reconcile meshx workflow schema: table %s exists but migration %s is not applied", table.workflow, table.version)
			}
			continue
		}
		if !workflowExists && !automationExists {
			return false, fmt.Errorf("reconcile meshx workflow schema: migration %s is applied but neither %s nor %s exists", table.version, table.workflow, table.automation)
		}
		legacyDetected = legacyDetected || workflowExists
	}
	if !legacyDetected {
		return false, nil
	}

	runsTable := "automation_runs"
	if db.Migrator().HasTable("workflow_runs") {
		runsTable = "workflow_runs"
	}
	for _, columns := range [][2]string{{"workflow_path", "definition_path"}, {"workflow_id", "definition_id"}} {
		workflowExists := db.Migrator().HasColumn(runsTable, columns[0])
		automationExists := db.Migrator().HasColumn(runsTable, columns[1])
		if workflowExists && automationExists {
			return false, fmt.Errorf("reconcile meshx workflow schema: %s has both %s and %s; refusing to overwrite either column", runsTable, columns[0], columns[1])
		}
		if !workflowExists && !automationExists {
			return false, fmt.Errorf("reconcile meshx workflow schema: %s has neither %s nor %s", runsTable, columns[0], columns[1])
		}
	}
	return true, nil
}

func ensureSchemaReconciliationsTable(db *gorm.DB) error {
	if err := db.Exec(`
CREATE TABLE IF NOT EXISTS schema_reconciliations (
  name VARCHAR(255) NOT NULL,
  applied_at TIMESTAMP NOT NULL,
  PRIMARY KEY (name)
)`).Error; err != nil {
		return fmt.Errorf("create meshx schema reconciliations table: %w", err)
	}
	return nil
}
