package infra

import (
	"fmt"
	"strconv"
	"strings"
)

// MigrationParse parses a migration filename and its SQL content into a Migration.
// The filename must follow the pattern NNN_name.sql (e.g. 001_create_users.sql).
// The content must contain a -- +up marker; -- +down is optional but recommended.
// Returns error if the filename format is invalid or the -- +up block is missing.
func MigrationParse(filename, content string) (Migration, error) {
	// Strip path prefix if any
	base := filename
	if idx := strings.LastIndex(base, "/"); idx >= 0 {
		base = base[idx+1:]
	}
	if idx := strings.LastIndex(base, "\\"); idx >= 0 {
		base = base[idx+1:]
	}

	// Remove .sql extension
	name := base
	if strings.HasSuffix(strings.ToLower(name), ".sql") {
		name = name[:len(name)-4]
	}

	// Split on first underscore to get version and descriptive name
	idx := strings.Index(name, "_")
	if idx < 0 {
		return Migration{}, fmt.Errorf("migration_parse: filename %q must follow pattern NNN_name.sql", filename)
	}

	versionStr := name[:idx]
	descriptiveName := name[idx+1:]

	version, err := strconv.Atoi(versionStr)
	if err != nil || version <= 0 {
		return Migration{}, fmt.Errorf("migration_parse: filename %q version %q must be a positive integer", filename, versionStr)
	}

	if descriptiveName == "" {
		return Migration{}, fmt.Errorf("migration_parse: filename %q must have a non-empty descriptive name after the version", filename)
	}

	// Parse up/down blocks
	upSQL, downSQL, err := parseMigrationBlocks(content)
	if err != nil {
		return Migration{}, fmt.Errorf("migration_parse: %w", err)
	}

	return Migration{
		Version: version,
		Name:    descriptiveName,
		UpSQL:   upSQL,
		DownSQL: downSQL,
	}, nil
}

// parseMigrationBlocks splits SQL content by -- +up and -- +down markers.
// Returns (upSQL, downSQL, error). The -- +up block is required.
func parseMigrationBlocks(content string) (string, string, error) {
	const markerUp = "-- +up"
	const markerDown = "-- +down"

	// Normalize line endings
	content = strings.ReplaceAll(content, "\r\n", "\n")

	upIdx := indexMarker(content, markerUp)
	if upIdx < 0 {
		return "", "", fmt.Errorf("missing -- +up marker in content")
	}

	downIdx := indexMarker(content, markerDown)

	var upSQL, downSQL string

	if downIdx < 0 {
		// No down block
		upSQL = strings.TrimSpace(content[upIdx+len(markerUp):])
	} else if downIdx > upIdx {
		// Normal order: up first, then down
		upSQL = strings.TrimSpace(content[upIdx+len(markerUp) : downIdx])
		downSQL = strings.TrimSpace(content[downIdx+len(markerDown):])
	} else {
		// Down before up — still valid, just unusual
		downSQL = strings.TrimSpace(content[downIdx+len(markerDown) : upIdx])
		upSQL = strings.TrimSpace(content[upIdx+len(markerUp):])
	}

	if upSQL == "" {
		return "", "", fmt.Errorf("-- +up block is empty")
	}

	return upSQL, downSQL, nil
}

// indexMarker finds the index of a marker at the start of any line in content.
func indexMarker(content, marker string) int {
	lines := strings.Split(content, "\n")
	pos := 0
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed == marker {
			return pos
		}
		pos += len(line) + 1 // +1 for the newline
	}
	return -1
}
