// Copyright (c) 2022 RethinkDNS and its authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package dnsx

import (
	"errors"

	x "github.com/celzero/firestack/intra/backend"
)

const (
	Start          = x.Start
	Complete       = x.Complete
	SendFailed     = x.SendFailed
	NoResponse     = x.NoResponse
	BadQuery       = x.BadQuery
	BadResponse    = x.BadResponse
	InternalError  = x.InternalError
	TransportError = x.TransportError
	ClientError    = x.ClientError
	Paused         = x.Paused
	DEnd           = x.DEnd
	Unknown        = 100
)

func Status2Str(status int) string {
	switch status {
	case Start:
		return "Starting"
	case Complete:
		return "OK"
	case SendFailed:
		return "Failing"
	case NoResponse:
		return "No Response"
	case BadQuery:
		return "Bad Query"
	case BadResponse:
		return "Misbehaving"
	case InternalError:
		return "Buggy"
	case TransportError:
		return "Refusing"
	case ClientError:
		return "Missing"
	case DEnd:
		return "End"
	case Paused:
		return "Paused"
	default:
		return "Unknown" // 100
	}
}

var errNop = errors.New("no error")

type QueryError struct {
	status int
	err    error
}

func (e *QueryError) Error() string {
	if e == nil || e.err == nil {
		return "[nil]"
	}
	return e.err.Error()
}

func (e *QueryError) Unwrap() error {
	if e == nil {
		return nil
	}
	return e.err // may be nil and that's how it should be
}

func (e *QueryError) Status() int {
	if e == nil {
		return Unknown // unknown
	}
	return e.status
}

func (e *QueryError) strstatus() string {
	if e == nil {
		return "[nil]"
	}
	return Status2Str(e.status)
}

func (e *QueryError) String() string {
	if e == nil {
		return "[nil]"
	}
	return e.strstatus() + ":" + e.Error()
}

func newQueryError(no int, err error) *QueryError {
	return &QueryError{no, err} // err may be nil
}

func NewSendFailedQueryError(err error) *QueryError {
	return newQueryError(SendFailed, err)
}

func NewNoResponseQueryError(err error) *QueryError {
	return newQueryError(NoResponse, err)
}

func NewInternalQueryError(err error) *QueryError {
	return newQueryError(InternalError, err)
}

func NewBadQueryError(err error) *QueryError {
	return newQueryError(BadQuery, err)
}

func NewBadResponseQueryError(err error) *QueryError {
	return newQueryError(BadResponse, err)
}

// with http, for 5xx errors
func NewTransportQueryError(err error) *QueryError {
	return newQueryError(TransportError, err)
}

// with http, for 4xx errors
func NewClientQueryError(err error) *QueryError {
	return newQueryError(ClientError, err)
}

func NewPausedQueryError() *QueryError {
	return newQueryError(Paused, errTransportPaused)
}

func NewEndQueryError() *QueryError {
	return newQueryError(DEnd, errTransportEnd)
}
