package libhelper

import (
	"fmt"
	"regexp"
	pl "simrs-vx/pkg/logger"
	"strings"
	"unicode"

	"github.com/jackc/pgx/v5/pgconn"
	"gorm.io/gorm"
)

func SearchCodeOrName(search string, tx *gorm.DB) *gorm.DB {
	if search != "" {
		tx = tx.Where("\"Code\" ILIKE ? OR \"Name\" ILIKE ?", "%"+search+"%", "%"+search+"%")
	}

	return tx
}

func Sort(sort string, tx *gorm.DB) *gorm.DB {
	if sort == "" {
		return tx
	}

	pairs := strings.Split(sort, ",")
	for _, pair := range pairs {
		parts := strings.Split(strings.TrimSpace(pair), ":")
		if len(parts) != 2 {
			continue // skip invalid format
		}

		field := strings.TrimSpace(parts[0])
		direction := strings.ToUpper(strings.TrimSpace(parts[1]))

		if direction != "ASC" && direction != "DESC" {
			continue
		}

		field = normalizeColumnName(field)

		tx = tx.Order(fmt.Sprintf("\"%s\" %s", field, direction))
	}

	return tx
}

func normalizeColumnName(input string) string {
	if input == "" {
		return input
	}

	input = strings.ReplaceAll(input, "-", "_")

	runes := []rune(input)
	var out []rune
	upperNext := true
	for _, r := range runes {
		if r == '_' {
			out = append(out, r)
			upperNext = true
			continue
		}
		if upperNext {
			out = append(out, unicode.ToUpper(r))
			upperNext = false
		} else {
			out = append(out, r)
		}
	}
	return string(out)
}

func HandleCreateError(input any, event *pl.Event, err error) error {
	e, ok := err.(*pgconn.PgError)
	if !ok {
		// fallback if it's not a pg error
		event.Status = "failed"
		event.ErrInfo = pl.ErrorInfo{
			Code:   "data-create-fail",
			Detail: fmt.Sprintf("Database insert failed: %s", err.Error()),
			Raw:    err,
		}
		return pl.SetLogError(event, input)
	}

	switch e.Code {
	case "23505": // unique_violation
		event.Status = "failed"
		event.ErrInfo = pl.ErrorInfo{
			Code:   "data-create-duplicate",
			Detail: fmt.Sprintf("Duplicate value violates unique constraint: %s", e.ConstraintName),
			Raw:    err,
		}
		return pl.SetLogError(event, input)

	case "23503": // foreign_key_violation
		event.Status = "failed"
		event.ErrInfo = pl.ErrorInfo{
			Code:   "data-create-fkViolation",
			Detail: fmt.Sprintf("Foreign key constraint '%s' violated. Please check related record exists.", e.ConstraintName),
			Raw:    err,
		}
		return pl.SetLogError(event, input)

	default:
		event.Status = "failed"
		event.ErrInfo = pl.ErrorInfo{
			Code:   "data-create-fail",
			Detail: fmt.Sprintf("Database insert failed: %s", e.Message),
			Raw:    err,
		}
		return pl.SetLogError(event, input)
	}
}

func HandleListError(input any, event *pl.Event, err error) error {
	if err == nil {
		return nil
	}

	e, ok := err.(*pgconn.PgError)
	if !ok {
		// fallback if it's not a pg error
		event.Status = "failed"
		event.ErrInfo = pl.ErrorInfo{
			Code:   "data-list-fail",
			Detail: fmt.Sprintf("database list failed: %s", err.Error()),
			Raw:    err,
		}
		return pl.SetLogError(event, input)
	}

	re := regexp.MustCompile(`^([^:]+):\s*unsupported relations for schema\s+(\S+)`)
	if matches := re.FindStringSubmatch(e.Message); len(matches) == 3 {
		relation := strings.TrimSpace(matches[1])
		schema := strings.TrimSpace(matches[2])

		event.Status = "failed"
		event.ErrInfo = pl.ErrorInfo{
			Code:   "invalid-relation",
			Detail: fmt.Sprintf("Invalid relation '%s' for schema '%s' — check the relation name.", relation, schema),
			Raw:    e,
		}
		return pl.SetLogError(event, input)
	}

	if strings.Contains(e.Message, "unsupported relations for schema") {
		event.Status = "failed"
		event.ErrInfo = pl.ErrorInfo{
			Code:   "invalid-relation",
			Detail: fmt.Sprintf("Unsupported relation detected: %s", e.Message),
			Raw:    e,
		}
		return pl.SetLogError(event, input)
	}

	// Generic pg error fallback
	event.Status = "failed"
	event.ErrInfo = pl.ErrorInfo{
		Code:   "pg-error",
		Detail: fmt.Sprintf("PostgreSQL error: %s", e.Message),
		Raw:    e,
	}
	return pl.SetLogError(event, input)
}
