// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package db

import (
	"fmt"
	"strings"

	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/util"
)

// ErrCancelled represents an error due to context cancellation
type ErrCancelled struct {
	Message string
}

// IsErrCancelled checks if an error is a ErrCancelled.
func IsErrCancelled(err error) bool {
	_, ok := err.(ErrCancelled)
	return ok
}

func (err ErrCancelled) Error() string {
	return "Cancelled: " + err.Message
}

// ErrCancelledf returns an ErrCancelled for the provided format and args
func ErrCancelledf(format string, args ...any) error {
	return ErrCancelled{
		fmt.Sprintf(format, args...),
	}
}

// ErrSSHDisabled represents an "SSH disabled" error.
type ErrSSHDisabled struct{}

// IsErrSSHDisabled checks if an error is a ErrSSHDisabled.
func IsErrSSHDisabled(err error) bool {
	_, ok := err.(ErrSSHDisabled)
	return ok
}

func (err ErrSSHDisabled) Error() string {
	return "SSH is disabled"
}

// ErrNotExist represents a non-exist error.
type ErrNotExist struct {
	Resource string
	ID       int64
}

// IsErrNotExist checks if an error is an ErrNotExist
func IsErrNotExist(err error) bool {
	_, ok := err.(ErrNotExist)
	return ok
}

func (err ErrNotExist) Error() string {
	name := "record"
	if err.Resource != "" {
		name = err.Resource
	}

	if err.ID != 0 {
		return fmt.Sprintf("%s does not exist [id: %d]", name, err.ID)
	}
	return name + " does not exist"
}

// Unwrap unwraps this as a ErrNotExist err
func (err ErrNotExist) Unwrap() error {
	return util.ErrNotExist
}

// IsErrDeadlock checks whether err is a database deadlock.
// MySQL returns error 1213 (ER_LOCK_DEADLOCK / SQLSTATE 40001).
// PostgreSQL returns SQLSTATE 40P01 with "deadlock detected".
// SQLite returns SQLITE_BUSY (error 5) with "database is locked".
func IsErrDeadlock(err error) bool {
	if err == nil {
		return false
	}
	msg := err.Error()
	// MySQL / MariaDB: "Error 1213 (40001): Deadlock found when trying to get lock"
	if strings.Contains(msg, "Error 1213") || strings.Contains(msg, "40001") {
		return true
	}
	// PostgreSQL: "deadlock detected"
	if strings.Contains(msg, "deadlock detected") {
		return true
	}
	// SQLite: "database is locked"
	if strings.Contains(msg, "database is locked") {
		return true
	}
	return false
}
