package cli

import (
	"fmt"
	"io"
	"os"
	"strings"
)

// query is a collection of queryParts which, when applied together, make up an
// executable SQL query.
type query []queryPart

func (q query) String() string {
	var sb strings.Builder
	for i := range q {
		sb.WriteString(q[i].String())
		if i < len(q)-1 {
			sb.WriteRune('\n')
		}
	}
	return sb.String()
}

// Reader returns the query as an io.Reader so that it can be passed to, for
// example, http.Post().
func (q query) Reader() io.Reader {
	readers := make([]io.Reader, 0, len(q))
	for i := range q {
		readers = append(readers, q[i].Reader())
	}
	return io.MultiReader(readers...)
}

// queryPart is an interface representing anything which can use to build up a
// query.
type queryPart interface {
	fmt.Stringer
	Reader() io.Reader
}

func newRawQuery(s string) query {
	return []queryPart{
		newPartRaw(s),
	}
}

// ////////////////////////////////////////////////////////////////////////////
// raw
// ////////////////////////////////////////////////////////////////////////////

// Ensure type implements interface.
var _ queryPart = (*partRaw)(nil)

type partRaw struct {
	raw string
}

func newPartRaw(s string) *partRaw {
	return &partRaw{
		raw: s,
	}
}

func (p *partRaw) Reader() io.Reader {
	return strings.NewReader(p.raw + "\n")
}

func (p *partRaw) String() string {
	return p.raw
}

// ////////////////////////////////////////////////////////////////////////////
// file
// ////////////////////////////////////////////////////////////////////////////

// Ensure type implements interface.
var _ queryPart = (*partFile)(nil)

type partFile struct {
	file *os.File
}

func newPartFile(f *os.File) *partFile {
	return &partFile{
		file: f,
	}
}

func (p *partFile) Reader() io.Reader {
	return p.file
}

func (p *partFile) String() string {
	return fmt.Sprintf("[file: %s]", p.file.Name())
}

// ////////////////////////////////////////////////////////////////////////////
// batch file
// ////////////////////////////////////////////////////////////////////////////

// Ensure type implements interface.
var _ queryPart = (*partBatchFile)(nil)

type partBatchFile struct {
	file *os.File
}

func (p *partBatchFile) Reader() io.Reader {
	return p.file
}

func (p *partBatchFile) String() string {
	return p.file.Name()
}

// ////////////////////////////////////////////////////////////////////////////
// terminator (i.e. ";")
// ////////////////////////////////////////////////////////////////////////////

// Ensure type implements interface.
var _ queryPart = (*partTerminator)(nil)

type partTerminator struct{}

func newPartTerminator() *partTerminator {
	return &partTerminator{}
}

func (p *partTerminator) Reader() io.Reader {
	return nil
}

func (p *partTerminator) String() string {
	return terminationChar
}
