package models

import (
	"fmt"
	"strings"

	"github.com/jmoiron/sqlx"
)

// Cards -
type Cards struct {
	ID            string   `db:"id" cc:"id"`
	Name          string   `db:"name" cc:"name"`
	CMC           float32  `db:"cmc" cc:"cmc"`
	ManaCost      string   `db:"mana_cost" cc:"mana_cost"`
	Set           string   `db:"set" cc:"set"`
	Text          string   `db:"text" cc:"text"`
	Layout        string   `db:"layout" cc:"layout"`
	ImageURL      string   `db:"image_url" cc:"image_url"`
	Loyalty       string   `db:"loyalty" cc:"loyalty"`
	Type          string   `db:"type" cc:"type"`
	Number        string   `db:"number" cc:"number"`
	Power         string   `db:"power" cc:"power"`
	Toughness     string   `db:"toughness" cc:"toughness"`
	Names         []string `db:"names" cc:"names"`
	Colors        []string `db:"colors" cc:"colors"`
	ColorIdentity []string `db:"color_identity" cc:"color_identity"`
	Types         []string `db:"types" cc:"types"`
	Subtypes      []string `db:"subtypes" cc:"subtypes"`
	Supertypes    []string `db:"supertypes" cc:"supertypes"`
}

// order opts
const (
	CardSearchOrderByName     OrderStr = `c."name" asc`
	CardSearchOrderByNameDesc          = `c."name" desc`
	CardSearchOrderByCMC               = `c."cmc" asc`
	CardSearchOrderByCMCDesc           = `c."cmc" desc"`
)

// CardSearchCMC -
type CardSearchCMC struct {
	Op  OpStr
	Val float32
}

// ListCardsOpts -
type ListCardsOpts struct {
	OrderBy OrderStr
	Name    string
	CMC     *CardSearchCMC
	Set     string
	Types   []string
	Limit   int
	Offset  int
}

// CountCards -
func CountCards(arg ListCardsOpts) (c int, err error) {
	c = 0
	query := `select count(*) as count from "public"."cards" c`
	where := struct {
		Name string  `db:"name"`
		CMC  float32 `db:"name"`
		Set  string  `db:"set"`
	}{}

	whereStr := []string{}
	if len(arg.Name) > 0 {
		where.Name = arg.Name
		whereStr = append(whereStr, `c."name" = :name`)
	}
	if len(arg.Set) > 0 {
		where.Set = arg.Set
		whereStr = append(whereStr, `c."set" = :set`)
	}
	if arg.CMC != nil {
		where.CMC = arg.CMC.Val
		whereStr = append(whereStr, fmt.Sprintf(`c."cmc" %s :cmc`, arg.CMC.Op))
	}

	wStr := ""
	if len(whereStr) > 0 {
		wStr = fmt.Sprintf("where %s", strings.Join(whereStr, " and "))
	}

	query = fmt.Sprintf("%s %s",
		query,
		wStr)
	q, a, err := sqlx.Named(query, where)
	if err != nil {
		return 0, err
	}
	err = x.Get(&c, q, a...)
	return
}

// ListCards -
func ListCards(arg ListCardsOpts) (cards []*Cards, err error) {
	if arg.Limit < 1 {
		arg.Limit = 1
	}
	if arg.Offset < 0 {
		arg.Offset = 0
	}
	query := `select c.id, c.name, c.cmc, c.mana_cost, c.text, c.layout, c.image_url, c.loyalty,
		c.type, c.number, c.power, c.toughness, c.set from "public"."cards" c`
	where := struct {
		Name string  `db:"name"`
		CMC  float32 `db:"cmc"`
		Set  string  `db:"set"`
	}{}
	whereStr := []string{}
	if len(arg.Name) > 0 {
		where.Name = arg.Name
		whereStr = append(whereStr, `c."name" = :name`)
	}
	if len(arg.Set) > 0 {
		where.Set = arg.Set
		whereStr = append(whereStr, `c."set" = :set`)
	}
	if arg.CMC != nil {
		where.CMC = arg.CMC.Val
		whereStr = append(whereStr, fmt.Sprintf(`c."cmc" %s :cmc`, arg.CMC.Op))
	}

	wStr := ""
	if len(whereStr) > 0 {
		wStr = fmt.Sprintf("where %s", strings.Join(whereStr, " and "))
	}

	query = fmt.Sprintf("%s %s %s %s",
		query,
		wStr,
		arg.OrderBy,
		fmt.Sprintf("limit %d offset %d", arg.Limit, arg.Offset))
	q, a, err := sqlx.Named(query, where)
	if err != nil {
		return nil, err
	}
	err = x.Select(&cards, q, a...)
	if err != nil {
		return nil, err
	}
	return
}
