package restore

import (
	"context"
	"fmt"
	"regexp"
	"strconv"
	"time"

	"dbbackup/internal/cleanup"
	"dbbackup/internal/database"
)

// VersionInfo holds PostgreSQL version information
type VersionInfo struct {
	Major int
	Minor int
	Full  string
}

// ParsePostgreSQLVersion extracts major and minor version from version string
// Example: "PostgreSQL 17.7 on x86_64-redhat-linux-gnu..." -> Major: 17, Minor: 7
func ParsePostgreSQLVersion(versionStr string) (*VersionInfo, error) {
	// Match patterns like "PostgreSQL 17.7", "PostgreSQL 13.11", "PostgreSQL 10.23"
	re := regexp.MustCompile(`PostgreSQL\s+(\d+)\.(\d+)`)
	matches := re.FindStringSubmatch(versionStr)

	if len(matches) < 3 {
		return nil, fmt.Errorf("could not parse PostgreSQL version from: %s", versionStr)
	}

	major, err := strconv.Atoi(matches[1])
	if err != nil {
		return nil, fmt.Errorf("invalid major version: %s", matches[1])
	}

	minor, err := strconv.Atoi(matches[2])
	if err != nil {
		return nil, fmt.Errorf("invalid minor version: %s", matches[2])
	}

	return &VersionInfo{
		Major: major,
		Minor: minor,
		Full:  versionStr,
	}, nil
}

// GetDumpFileVersion extracts the PostgreSQL version from a dump file
// Uses pg_restore -l to read the dump metadata
// Uses a 30-second timeout to avoid blocking on large files
func GetDumpFileVersion(dumpPath string) (*VersionInfo, error) {
	// Use a timeout context to prevent blocking on very large dump files
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	cmd := cleanup.SafeCommand(ctx, "pg_restore", "-l", dumpPath)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("failed to read dump file metadata: %w (output: %s)", err, string(output))
	}

	// Look for "Dumped from database version: X.Y.Z" in output
	re := regexp.MustCompile(`Dumped from database version:\s+(\d+)\.(\d+)`)
	matches := re.FindStringSubmatch(string(output))

	if len(matches) < 3 {
		// Try alternate format in some dumps
		re = regexp.MustCompile(`PostgreSQL database dump.*(\d+)\.(\d+)`)
		matches = re.FindStringSubmatch(string(output))
	}

	if len(matches) < 3 {
		return nil, fmt.Errorf("could not find version information in dump file")
	}

	major, _ := strconv.Atoi(matches[1])
	minor, _ := strconv.Atoi(matches[2])

	return &VersionInfo{
		Major: major,
		Minor: minor,
		Full:  fmt.Sprintf("PostgreSQL %d.%d", major, minor),
	}, nil
}

// CheckVersionCompatibility checks if restoring from source version to target version is safe
func CheckVersionCompatibility(sourceVer, targetVer *VersionInfo) *VersionCompatibilityResult {
	result := &VersionCompatibilityResult{
		Compatible:    true,
		SourceVersion: sourceVer,
		TargetVersion: targetVer,
	}

	// Same major version - always compatible
	if sourceVer.Major == targetVer.Major {
		result.Level = CompatibilityLevelSafe
		result.Message = "Same major version - fully compatible"
		return result
	}

	// Downgrade - not supported
	if sourceVer.Major > targetVer.Major {
		result.Compatible = false
		result.Level = CompatibilityLevelUnsupported
		result.Message = fmt.Sprintf("Downgrade from PostgreSQL %d to %d is not supported", sourceVer.Major, targetVer.Major)
		result.Warnings = append(result.Warnings, "Database downgrades require pg_dump from the target version")
		return result
	}

	// Upgrade - check how many major versions
	versionDiff := targetVer.Major - sourceVer.Major

	if versionDiff == 1 {
		// One major version upgrade - generally safe
		result.Level = CompatibilityLevelSafe
		result.Message = fmt.Sprintf("Upgrading from PostgreSQL %d to %d - officially supported", sourceVer.Major, targetVer.Major)
	} else if versionDiff <= 3 {
		// 2-3 major versions - should work but review release notes
		result.Level = CompatibilityLevelWarning
		result.Message = fmt.Sprintf("Upgrading from PostgreSQL %d to %d - supported but review release notes", sourceVer.Major, targetVer.Major)
		result.Warnings = append(result.Warnings,
			fmt.Sprintf("You are jumping %d major versions - some features may have changed", versionDiff))
		result.Warnings = append(result.Warnings,
			"Review release notes for deprecated features or behavior changes")
	} else {
		// 4+ major versions - high risk
		result.Level = CompatibilityLevelRisky
		result.Message = fmt.Sprintf("Upgrading from PostgreSQL %d to %d - large version jump", sourceVer.Major, targetVer.Major)
		result.Warnings = append(result.Warnings,
			fmt.Sprintf("WARNING: Jumping %d major versions may encounter compatibility issues", versionDiff))
		result.Warnings = append(result.Warnings,
			"Deprecated features from PostgreSQL "+strconv.Itoa(sourceVer.Major)+" may not exist in "+strconv.Itoa(targetVer.Major))
		result.Warnings = append(result.Warnings,
			"Extensions may need updates or may be incompatible")
		result.Warnings = append(result.Warnings,
			"Test thoroughly in a non-production environment first")
		result.Recommendations = append(result.Recommendations,
			"Consider using --schema-only first to validate schema compatibility")
		result.Recommendations = append(result.Recommendations,
			"Review PostgreSQL release notes for versions "+strconv.Itoa(sourceVer.Major)+" through "+strconv.Itoa(targetVer.Major))
	}

	// Add general upgrade advice
	if versionDiff > 0 {
		result.Recommendations = append(result.Recommendations,
			"Run ANALYZE on all tables after restore for optimal query performance")
	}

	return result
}

// CompatibilityLevel indicates the risk level of version compatibility
type CompatibilityLevel int

const (
	CompatibilityLevelSafe CompatibilityLevel = iota
	CompatibilityLevelWarning
	CompatibilityLevelRisky
	CompatibilityLevelUnsupported
)

func (c CompatibilityLevel) String() string {
	switch c {
	case CompatibilityLevelSafe:
		return "SAFE"
	case CompatibilityLevelWarning:
		return "WARNING"
	case CompatibilityLevelRisky:
		return "RISKY"
	case CompatibilityLevelUnsupported:
		return "UNSUPPORTED"
	default:
		return "UNKNOWN"
	}
}

// VersionCompatibilityResult contains the result of version compatibility check
type VersionCompatibilityResult struct {
	Compatible      bool
	Level           CompatibilityLevel
	SourceVersion   *VersionInfo
	TargetVersion   *VersionInfo
	Message         string
	Warnings        []string
	Recommendations []string
}

// CheckRestoreVersionCompatibility performs version check for a restore operation
func (e *Engine) CheckRestoreVersionCompatibility(ctx context.Context, dumpPath string) (*VersionCompatibilityResult, error) {
	// Get dump file version
	dumpVer, err := GetDumpFileVersion(dumpPath)
	if err != nil {
		// Not critical if we can't read version - continue with warning
		e.log.Warn("Could not determine dump file version", "error", err)
		return nil, nil
	}

	// Get target database version
	targetVerStr, err := e.db.GetVersion(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get target database version: %w", err)
	}

	targetVer, err := ParsePostgreSQLVersion(targetVerStr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse target version: %w", err)
	}

	// Check compatibility
	result := CheckVersionCompatibility(dumpVer, targetVer)

	// Log the results
	e.log.Info("Version compatibility check",
		"source", dumpVer.Full,
		"target", targetVer.Full,
		"level", result.Level.String())

	if len(result.Warnings) > 0 {
		for _, warning := range result.Warnings {
			e.log.Warn(warning)
		}
	}

	return result, nil
}

// ValidatePostgreSQLDatabase ensures we're working with a PostgreSQL database
func ValidatePostgreSQLDatabase(db database.Database) error {
	// Type assertion to check if it's PostgreSQL
	switch db.(type) {
	case *database.PostgreSQL:
		return nil
	default:
		return fmt.Errorf("version compatibility checks only supported for PostgreSQL")
	}
}
