// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sql

import (
	"strconv"
	"strings"
	"time"

	"golang.org/x/text/language"
)

// --------------------------------------------------
// Queries
// --------------------------------------------------

// Query represents a multi statement SQL query
type Query struct {
	Statements Statements
}

// Statement represents a single SQL AST
type Statement interface{}

// Statements represents multiple SQL ASTs
type Statements []Statement

// --------------------------------------------------
// Other
// --------------------------------------------------

type AuthableStatement interface {
	Auth() (string, string)
}

type KillableStatement interface {
	Duration() time.Duration
}

type WriteableStatement interface {
	Writeable() bool
}

// --------------------------------------------------
// Use
// --------------------------------------------------

// UseStatement represents a SQL USE statement.
type UseStatement struct {
	NS string
	DB string
}

// --------------------------------------------------
// Options
// --------------------------------------------------

// OptStatement represents a SQL OPTION statement.
type OptStatement struct {
	Name string
	What bool
}

// --------------------------------------------------
// Trans
// --------------------------------------------------

// BeginStatement represents a SQL BEGIN TRANSACTION statement.
type BeginStatement struct{}

// CancelStatement represents a SQL CANCEL TRANSACTION statement.
type CancelStatement struct{}

// CommitStatement represents a SQL COMMIT TRANSACTION statement.
type CommitStatement struct{}

// --------------------------------------------------
// Info
// --------------------------------------------------

// InfoStatement represents an SQL INFO statement.
type InfoStatement struct {
	Kind Token
	What *Ident
}

// --------------------------------------------------
// Run
// --------------------------------------------------

// RunStatement represents a run clause.
type RunStatement struct {
	RW   bool
	Expr Expr
}

// --------------------------------------------------
// Let
// --------------------------------------------------

// LetStatement represents a SQL LET statement.
type LetStatement struct {
	RW   bool
	Name *Ident
	What Expr
}

// --------------------------------------------------
// Live
// --------------------------------------------------

// LiveStatement represents a SQL LIVE statement.
type LiveStatement struct {
	ID    string
	FB    string
	NS    string
	DB    string
	Diff  bool
	Expr  Fields
	What  Exprs
	Cond  Expr
	Fetch Fetchs
}

// KillStatement represents a SQL KILL statement.
type KillStatement struct {
	FB   string
	What Exprs
}

// --------------------------------------------------
// Normal
// --------------------------------------------------

// ReturnStatement represents a SQL RETURN statement.
type ReturnStatement struct {
	RW   bool
	What Exprs
}

// IfelseStatement represents a SQL IFELSE statement.
type IfelseStatement struct {
	RW   bool
	Cond Exprs
	Then Exprs
	Else Expr
}

// SelectStatement represents a SQL SELECT statement.
type SelectStatement struct {
	RW       bool
	Expr     Fields
	What     Exprs
	Cond     Expr
	Split    Idents
	Group    Groups
	Order    Orders
	Limit    Expr
	Start    Expr
	Fetch    Fetchs
	Version  Expr
	Timeout  time.Duration
	Parallel bool
}

// CreateStatement represents a SQL CREATE statement.
type CreateStatement struct {
	What     Exprs
	Data     Expr
	Echo     Token
	Timeout  time.Duration
	Parallel bool
}

// UpdateStatement represents a SQL UPDATE statement.
type UpdateStatement struct {
	What     Exprs
	Data     Expr
	Cond     Expr
	Echo     Token
	Timeout  time.Duration
	Parallel bool
}

// DeleteStatement represents a SQL DELETE statement.
type DeleteStatement struct {
	What     Exprs
	Cond     Expr
	Echo     Token
	Timeout  time.Duration
	Parallel bool
}

// RelateStatement represents a SQL RELATE statement.
type RelateStatement struct {
	Type     Expr
	From     Exprs
	With     Exprs
	Data     Expr
	Uniq     bool
	Echo     Token
	Timeout  time.Duration
	Parallel bool
}

// InsertStatement represents a SQL INSERT statement.
type InsertStatement struct {
	Data     Expr
	Into     *Table
	Echo     Token
	Timeout  time.Duration
	Parallel bool
}

// UpsertStatement represents a SQL UPSERT statement.
type UpsertStatement struct {
	Data     Expr
	Into     *Table
	Echo     Token
	Timeout  time.Duration
	Parallel bool
}

// --------------------------------------------------
// Namespace
// --------------------------------------------------

type DefineNamespaceStatement struct {
	Name *Ident
}

type RemoveNamespaceStatement struct {
	Name *Ident
}

// --------------------------------------------------
// Database
// --------------------------------------------------

type DefineDatabaseStatement struct {
	Name *Ident
}

type RemoveDatabaseStatement struct {
	Name *Ident
}

// --------------------------------------------------
// Login
// --------------------------------------------------

// DefineLoginStatement represents an SQL DEFINE LOGIN statement.
type DefineLoginStatement struct {
	Kind Token
	User *Ident
	Pass []byte
	Hash []byte
	Code []byte
}

// RemoveLoginStatement represents an SQL REMOVE LOGIN statement.
type RemoveLoginStatement struct {
	Kind Token
	User *Ident
}

// --------------------------------------------------
// Token
// --------------------------------------------------

// DefineTokenStatement represents an SQL DEFINE TOKEN statement.
type DefineTokenStatement struct {
	Kind Token
	Name *Ident
	What *Ident
	Type string
	Code []byte
}

// RemoveTokenStatement represents an SQL REMOVE TOKEN statement.
type RemoveTokenStatement struct {
	Kind Token
	Name *Ident
	What *Ident
}

// --------------------------------------------------
// Scope
// --------------------------------------------------

// DefineScopeStatement represents an SQL DEFINE SCOPE statement.
type DefineScopeStatement struct {
	Name     *Ident
	Time     time.Duration
	Code     []byte
	Signup   Expr
	Signin   Expr
	Connect  Expr
	OnSignup Expr
	OnSignin Expr
}

// RemoveScopeStatement represents an SQL REMOVE SCOPE statement.
type RemoveScopeStatement struct {
	Name *Ident
}

// --------------------------------------------------
// Table
// --------------------------------------------------

// DefineTableStatement represents an SQL DEFINE TABLE statement.
type DefineTableStatement struct {
	Name  *Ident
	What  Tables
	Full  bool
	Vers  bool
	Drop  bool
	Lock  bool
	Expr  Fields
	From  Tables
	Cond  Expr
	Group Groups
	Perms Expr
}

// RemoveTableStatement represents an SQL REMOVE TABLE statement.
type RemoveTableStatement struct {
	What Tables
}

// --------------------------------------------------
// Event
// --------------------------------------------------

// DefineEventStatement represents an SQL DEFINE EVENT statement.
type DefineEventStatement struct {
	Name *Ident
	What Tables
	When Expr
	Then Expr
}

// RemoveEventStatement represents an SQL REMOVE EVENT statement.
type RemoveEventStatement struct {
	Name *Ident
	What Tables
}

// --------------------------------------------------
// Field
// --------------------------------------------------

// DefineFieldStatement represents an SQL DEFINE FIELD statement.
type DefineFieldStatement struct {
	Name     *Ident
	What     Tables
	Perms    Expr
	Type     string
	Kind     string
	Value    Expr
	Assert   Expr
	Priority float64
}

// RemoveFieldStatement represents an SQL REMOVE FIELD statement.
type RemoveFieldStatement struct {
	Name *Ident
	What Tables
}

// --------------------------------------------------
// Index
// --------------------------------------------------

// DefineIndexStatement represents an SQL DEFINE INDEX statement.
type DefineIndexStatement struct {
	Name *Ident
	What Tables
	Cols Idents
	Uniq bool
}

// RemoveIndexStatement represents an SQL REMOVE INDEX statement.
type RemoveIndexStatement struct {
	Name *Ident
	What Tables
}

// --------------------------------------------------
// Literals
// --------------------------------------------------

// Expr represents a sql expression.
type Expr interface{}

// Exprs represents multiple sql expressions.
type Exprs []Expr

// All represents a * expression.
type All struct{}

// Any represents a ? expression.
type Any struct{}

// Null represents an expression which is null.
type Null struct{}

// Void represents an expression which is not set.
type Void struct{}

// Empty represents an expression which is null or "".
type Empty struct{}

// Field represents a SELECT AS clause.
type Field struct {
	Expr  Expr
	Field string
	Alias string
}

// Fields represents multiple SELECT AS clauses.
type Fields []*Field

// Group represents a GROUP BY clause.
type Group struct {
	Expr Expr
}

// Groups represents multiple GROUP BY clauses.
type Groups []*Group

// Order represents a ORDER BY clause.
type Order struct {
	Expr Expr
	Dir  bool
	Tag  language.Tag
}

// Orders represents multiple ORDER BY clauses.
type Orders []*Order

// Fetch represents a FETCH AS clause.
type Fetch struct {
	Expr Expr
}

// Fetchs represents multiple FETCH AS clauses.
type Fetchs []*Fetch

// --------------------------------------------------
// Expressions
// --------------------------------------------------

// SubExpression represents a subquery.
type SubExpression struct {
	Expr Expr
}

// MultExpression represents multiple queries.
type MultExpression struct {
	Expr []Expr
}

// FuncExpression represents a function call.
type FuncExpression struct {
	Name string
	Args Exprs
	Aggr bool
}

// ItemExpression represents a part of a SET expression.
type ItemExpression struct {
	LHS Expr
	Op  Token
	RHS Expr
}

// BinaryExpression represents a WHERE expression.
type BinaryExpression struct {
	LHS Expr
	Op  Token
	RHS Expr
}

// PathExpression represents a path expression.
type PathExpression struct {
	Expr Exprs
}

// PartExpression represents a path part expression.
type PartExpression struct {
	Part Expr
}

// JoinExpression represents a path join expression.
type JoinExpression struct {
	Join Token
}

// SubpExpression represents a sub path expression.
type SubpExpression struct {
	What Exprs
	Name *Ident
	Cond Expr
}

// PermExpression represents a permissions expression.
type PermExpression struct {
	Select Expr
	Create Expr
	Update Expr
	Delete Expr
}

// DataExpression represents a SET expression.
type DataExpression struct {
	Data []*ItemExpression
}

// DiffExpression represents a JSON to DIFF
type DiffExpression struct {
	Data Expr
}

// MergeExpression represents JSON to MERGE
type MergeExpression struct {
	Data Expr
}

// ContentExpression represents JSON to REPLACE
type ContentExpression struct {
	Data Expr
}

// --------------------------------------------------
// Param
// --------------------------------------------------

// Params represents multiple Param clauses.
type Params []*Param

// Param comment
type Param struct {
	VA string
}

func NewParam(VA string) *Param {
	return &Param{VA}
}

// --------------------------------------------------
// Ident
// --------------------------------------------------

// Idents represents multiple Ident clauses.
type Idents []*Ident

// Ident comment
type Ident struct {
	VA string
}

func NewIdent(VA string) *Ident {
	return &Ident{VA}
}

// --------------------------------------------------
// Value
// --------------------------------------------------

// Values represents multiple Value clauses.
type Values []*Value

// Value comment
type Value struct {
	VA string
}

func NewValue(VA string) *Value {
	return &Value{VA}
}

// --------------------------------------------------
// Regex
// --------------------------------------------------

// Regexs represents multiple Regex clauses.
type Regexs []*Regex

// Regex comment
type Regex struct {
	VA string
}

func NewRegex(VA string) *Regex {
	return &Regex{VA}
}

// --------------------------------------------------
// Table
// --------------------------------------------------

// Tables represents multiple Table clauses.
type Tables []*Table

// Table comment
type Table struct {
	TB string
}

func NewTable(TB string) *Table {
	return &Table{TB}
}

// --------------------------------------------------
// Batch
// --------------------------------------------------

// Batchs represents multiple Batch clauses.
type Batchs []*Batch

// Batch comment
type Batch struct {
	TB string
	BA []*Thing
}

func NewBatch(TB string, BA []interface{}) *Batch {
	var b = &Batch{TB: TB}
	for _, ID := range BA {
		b.BA = append(b.BA, NewThing(TB, ID))
	}
	return b
}

// --------------------------------------------------
// Model
// --------------------------------------------------

// Models represents multiple Model clauses.
type Models []*Model

// Model comment
type Model struct {
	TB  string
	MIN float64
	INC float64
	MAX float64
}

func NewModel(TB string, MIN, INC, MAX float64) *Model {
	return &Model{TB: TB, MIN: MIN, INC: INC, MAX: MAX}
}

// --------------------------------------------------
// Thing
// --------------------------------------------------

// Things represents multiple Thing clauses.
type Things []*Thing

// Thing comment
type Thing struct {
	TB string
	ID interface{}
}

func ParseThing(val string) *Thing {
	r := strings.NewReader(val)
	s := newScanner(r)
	if t, _, v := s.scanIdiom(); t == THING {
		return v.(*Thing)
	}
	return nil
}

func NewThing(TB string, ID interface{}) *Thing {
	switch val := ID.(type) {
	default:
		return &Thing{TB: TB, ID: ID}
	case int:
		return &Thing{TB: TB, ID: float64(val)}
	case int64:
		return &Thing{TB: TB, ID: float64(val)}
	case string:
		val = strings.Replace(val, TB+":", "", -1)
		if cnv, err := strconv.ParseFloat(val, 64); err == nil {
			return &Thing{TB: TB, ID: cnv}
		} else if cnv, err := strconv.ParseBool(val); err == nil {
			return &Thing{TB: TB, ID: cnv}
		} else if cnv, err := time.Parse(RFCDate, val); err == nil {
			return &Thing{TB: TB, ID: cnv.UTC()}
		} else if cnv, err := time.Parse(RFCTime, val); err == nil {
			return &Thing{TB: TB, ID: cnv.UTC()}
		}
		return &Thing{TB: TB, ID: val}
	}
}

// --------------------------------------------------
// Point
// --------------------------------------------------

// Points represents multiple Point clauses.
type Points []*Point

// Point comment
type Point struct {
	LA float64
	LO float64
}

func NewPoint(LA, LO float64) *Point {
	return &Point{LA: LA, LO: LO}
}

// --------------------------------------------------
// Circle
// --------------------------------------------------

// Circles represents multiple Circle clauses.
type Circles []*Circle

// Circle comment
type Circle struct {
	CE *Point
	RA float64
}

func NewCircle(CE *Point, RA float64) *Circle {
	return &Circle{CE: CE, RA: RA}
}

// --------------------------------------------------
// Polygon
// --------------------------------------------------

// Polygons represents multiple Polygon clauses.
type Polygons []*Polygon

// Polygon comment
type Polygon struct {
	PS []*Point
}

func NewPolygon(PS ...*Point) *Polygon {
	return &Polygon{PS: PS}
}