package tools

import (
	"context"
	"database/sql"
	"fmt"
	"skraak/db"
	"skraak/utils"
	"strings"
)

// PatternInput defines the input parameters for the create_or_update_pattern tool
type PatternInput struct {
	ID            *string `json:"id,omitempty"`
	RecordSeconds *int    `json:"record_seconds,omitempty"`
	SleepSeconds  *int    `json:"sleep_seconds,omitempty"`
}

// PatternOutput defines the output structure
type PatternOutput struct {
	Pattern db.CyclicRecordingPattern `json:"pattern"`
	Message string                    `json:"message"`
}

// CreateOrUpdatePattern creates a new recording pattern or updates an existing one
func CreateOrUpdatePattern(
	ctx context.Context,
	input PatternInput,
) (PatternOutput, error) {
	if input.ID != nil && strings.TrimSpace(*input.ID) != "" {
		return updatePattern(ctx, input)
	}
	return createPattern(ctx, input)
}

// validateCreatePatternInput validates required fields for pattern creation.
func validateCreatePatternInput(input PatternInput) error {
	if input.RecordSeconds == nil {
		return fmt.Errorf("record_seconds is required when creating a pattern")
	}
	if input.SleepSeconds == nil {
		return fmt.Errorf("sleep_seconds is required when creating a pattern")
	}
	if err := utils.ValidatePositive(*input.RecordSeconds, "record_seconds"); err != nil {
		return err
	}
	return utils.ValidatePositive(*input.SleepSeconds, "sleep_seconds")
}

// findExistingPattern checks if an active pattern with the given record/sleep times exists.
func findExistingPattern(ctx context.Context, tx *db.LoggedTx, recordSeconds, sleepSeconds int) (db.CyclicRecordingPattern, bool, error) {
	var existingID string
	err := tx.QueryRowContext(ctx,
		"SELECT id FROM cyclic_recording_pattern WHERE record_s = ? AND sleep_s = ? AND active = true",
		recordSeconds, sleepSeconds,
	).Scan(&existingID)

	if err == sql.ErrNoRows {
		return db.CyclicRecordingPattern{}, false, nil
	}
	if err != nil {
		return db.CyclicRecordingPattern{}, false, err
	}

	var pattern db.CyclicRecordingPattern
	err = tx.QueryRowContext(ctx,
		"SELECT id, record_s, sleep_s, created_at, last_modified, active FROM cyclic_recording_pattern WHERE id = ?",
		existingID,
	).Scan(&pattern.ID, &pattern.RecordS, &pattern.SleepS, &pattern.CreatedAt, &pattern.LastModified, &pattern.Active)
	if err != nil {
		return db.CyclicRecordingPattern{}, false, fmt.Errorf("failed to fetch existing pattern: %w", err)
	}
	return pattern, true, nil
}

// validateUpdatePatternInput validates fields for pattern update.
func validateUpdatePatternInput(input PatternInput) error {
	if err := utils.ValidateShortID(*input.ID, "pattern_id"); err != nil {
		return err
	}
	if input.RecordSeconds != nil {
		if err := utils.ValidatePositive(*input.RecordSeconds, "record_seconds"); err != nil {
			return err
		}
	}
	if input.SleepSeconds != nil {
		if err := utils.ValidateNonNegative(*input.SleepSeconds, "sleep_seconds"); err != nil {
			return err
		}
	}
	return nil
}

func createPattern(ctx context.Context, input PatternInput) (PatternOutput, error) {
	var output PatternOutput

	if err := validateCreatePatternInput(input); err != nil {
		return output, err
	}

	err := db.WithWriteTx(ctx, dbPath, "create_or_update_pattern", func(database *sql.DB, tx *db.LoggedTx) error {
		// Check if pattern with same record_s/sleep_s already exists
		existing, found, ferr := findExistingPattern(ctx, tx, *input.RecordSeconds, *input.SleepSeconds)
		if ferr != nil {
			return fmt.Errorf("failed to check for existing pattern: %w", ferr)
		}
		if found {
			output.Pattern = existing
			output.Message = fmt.Sprintf("Pattern already exists with ID %s (record %ds, sleep %ds) - returning existing pattern",
				existing.ID, existing.RecordS, existing.SleepS)
			return nil // commit transaction
		}

		// Generate ID
		id, gerr := utils.GenerateShortID()
		if gerr != nil {
			return fmt.Errorf("failed to generate ID: %w", gerr)
		}

		// Insert pattern
		if _, err := tx.ExecContext(ctx,
			"INSERT INTO cyclic_recording_pattern (id, record_s, sleep_s, created_at, last_modified, active) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, TRUE)",
			id, *input.RecordSeconds, *input.SleepSeconds,
		); err != nil {
			return fmt.Errorf("failed to create pattern: %w", err)
		}

		// Fetch the created pattern
		var pattern db.CyclicRecordingPattern
		if err := tx.QueryRowContext(ctx,
			"SELECT id, record_s, sleep_s, created_at, last_modified, active FROM cyclic_recording_pattern WHERE id = ?",
			id,
		).Scan(&pattern.ID, &pattern.RecordS, &pattern.SleepS, &pattern.CreatedAt, &pattern.LastModified, &pattern.Active); err != nil {
			return fmt.Errorf("failed to fetch created pattern: %w", err)
		}

		output.Pattern = pattern
		output.Message = fmt.Sprintf("Successfully created cyclic recording pattern with ID %s (record %ds, sleep %ds)",
			pattern.ID, pattern.RecordS, pattern.SleepS)
		return nil
	})
	return output, err
}

// verifyPatternExistsAndActive checks that a pattern exists and is active.
func verifyPatternExistsAndActive(database *sql.DB, patternID string) error {
	var exists, active bool
	err := database.QueryRow(
		"SELECT EXISTS(SELECT 1 FROM cyclic_recording_pattern WHERE id = ?), COALESCE((SELECT active FROM cyclic_recording_pattern WHERE id = ?), false)",
		patternID, patternID,
	).Scan(&exists, &active)
	if err != nil {
		return fmt.Errorf("failed to query pattern: %w", err)
	}
	if !exists {
		return fmt.Errorf("pattern not found: %s", patternID)
	}
	if !active {
		return fmt.Errorf("pattern '%s' is not active (cannot update inactive patterns)", patternID)
	}
	return nil
}

// buildPatternUpdateQuery builds the dynamic UPDATE query and args for pattern fields.
func buildPatternUpdateQuery(input PatternInput) (string, []any, error) {
	updates := []string{}
	args := []any{}

	if input.RecordSeconds != nil {
		updates = append(updates, "record_s = ?")
		args = append(args, *input.RecordSeconds)
	}
	if input.SleepSeconds != nil {
		updates = append(updates, "sleep_s = ?")
		args = append(args, *input.SleepSeconds)
	}

	if len(updates) == 0 {
		return "", nil, fmt.Errorf("no fields provided to update")
	}

	updates = append(updates, "last_modified = now()")
	args = append(args, *input.ID)

	query := fmt.Sprintf("UPDATE cyclic_recording_pattern SET %s WHERE id = ?", strings.Join(updates, ", "))
	return query, args, nil
}

func updatePattern(ctx context.Context, input PatternInput) (PatternOutput, error) {
	var output PatternOutput

	if err := validateUpdatePatternInput(input); err != nil {
		return output, err
	}

	err := db.WithWriteTx(ctx, dbPath, "create_or_update_pattern", func(database *sql.DB, tx *db.LoggedTx) error {
		if err := verifyPatternExistsAndActive(database, *input.ID); err != nil {
			return err
		}

		query, args, qerr := buildPatternUpdateQuery(input)
		if qerr != nil {
			return qerr
		}

		if _, err := tx.Exec(query, args...); err != nil {
			return fmt.Errorf("failed to update pattern: %w", err)
		}

		// Fetch the updated pattern
		var pattern db.CyclicRecordingPattern
		if err := tx.QueryRow(
			"SELECT id, record_s, sleep_s, created_at, last_modified, active FROM cyclic_recording_pattern WHERE id = ?",
			*input.ID,
		).Scan(&pattern.ID, &pattern.RecordS, &pattern.SleepS, &pattern.CreatedAt, &pattern.LastModified, &pattern.Active); err != nil {
			return fmt.Errorf("failed to fetch updated pattern: %w", err)
		}

		output.Pattern = pattern
		output.Message = fmt.Sprintf("Successfully updated pattern (ID: %s, record %ds, sleep %ds)",
			pattern.ID, pattern.RecordS, pattern.SleepS)
		return nil
	})
	return output, err
}