package mdb

import (
	"context"
	_ "embed"
	"fmt"
	"strconv"
	"strings"

	"zombiezen.com/go/sqlite"
	"zombiezen.com/go/sqlite/sqlitex"

	"git.sunturtle.xyz/zephyr/horse"
)

var (
	//go:embed sql/skill-group.sql
	skillGroupSQL string
	//go:embed sql/skill.sql
	skillSQL string
)

// SkillGroups retrieves all skill groups.
func SkillGroups(ctx context.Context, db *sqlitex.Pool) ([]horse.SkillGroup, error) {
	return load(ctx, db, nil, skillGroupSQL, func(s *sqlite.Stmt) horse.SkillGroup {
		return horse.SkillGroup{
			ID:       horse.SkillGroupID(s.ColumnInt(0)),
			Skill1:   horse.SkillID(s.ColumnInt(1)),
			Skill2:   horse.SkillID(s.ColumnInt(2)),
			Skill3:   horse.SkillID(s.ColumnInt(3)),
			SkillBad: horse.SkillID(s.ColumnInt(4)),
		}
	})
}

// Skills retrieves all skills.
func Skills(ctx context.Context, db *sqlitex.Pool) ([]horse.Skill, error) {
	return load(ctx, db, nil, skillSQL, func(s *sqlite.Stmt) horse.Skill {
		return horse.Skill{
			ID:          horse.SkillID(s.ColumnInt(0)),
			Name:        s.ColumnText(1),
			Description: s.ColumnText(2),
			Group:       horse.SkillGroupID(s.ColumnInt32(3)),
			Rarity:      int8(s.ColumnInt(5)),
			GroupRate:   int8(s.ColumnInt(6)),
			GradeValue:  s.ColumnInt32(7),
			WitCheck:    s.ColumnBool(8),
			Activations: trimActivations([]horse.Activation{
				{
					Precondition: s.ColumnText(9),
					Condition:    s.ColumnText(10),
					Duration:     horse.TenThousandths(s.ColumnInt(11)),
					DurScale:     horse.DurScale(s.ColumnInt(12)),
					Cooldown:     horse.TenThousandths(s.ColumnInt(13)),
					Abilities: trimAbilities([]horse.Ability{
						{
							Type:        horse.AbilityType(s.ColumnInt(14)),
							ValueUsage:  horse.AbilityValueUsage(s.ColumnInt(15)),
							Value:       horse.TenThousandths(s.ColumnInt(16)),
							Target:      horse.AbilityTarget(s.ColumnInt(17)),
							TargetValue: s.ColumnInt32(18),
						},
						{
							Type:        horse.AbilityType(s.ColumnInt(19)),
							ValueUsage:  horse.AbilityValueUsage(s.ColumnInt(20)),
							Value:       horse.TenThousandths(s.ColumnInt(21)),
							Target:      horse.AbilityTarget(s.ColumnInt(22)),
							TargetValue: s.ColumnInt32(23),
						},
						{
							Type:        horse.AbilityType(s.ColumnInt(24)),
							ValueUsage:  horse.AbilityValueUsage(s.ColumnInt(25)),
							Value:       horse.TenThousandths(s.ColumnInt(26)),
							Target:      horse.AbilityTarget(s.ColumnInt(27)),
							TargetValue: s.ColumnInt32(28),
						},
					}),
				},
				{
					Precondition: s.ColumnText(29),
					Condition:    s.ColumnText(30),
					Duration:     horse.TenThousandths(s.ColumnInt(31)),
					DurScale:     horse.DurScale(s.ColumnInt(32)),
					Cooldown:     horse.TenThousandths(s.ColumnInt(33)),
					Abilities: trimAbilities([]horse.Ability{
						{
							Type:        horse.AbilityType(s.ColumnInt(34)),
							ValueUsage:  horse.AbilityValueUsage(s.ColumnInt(35)),
							Value:       horse.TenThousandths(s.ColumnInt(36)),
							Target:      horse.AbilityTarget(s.ColumnInt(37)),
							TargetValue: s.ColumnInt32(38),
						},
						{
							Type:        horse.AbilityType(s.ColumnInt(39)),
							ValueUsage:  horse.AbilityValueUsage(s.ColumnInt(40)),
							Value:       horse.TenThousandths(s.ColumnInt(41)),
							Target:      horse.AbilityTarget(s.ColumnInt(42)),
							TargetValue: s.ColumnInt32(43),
						},
						{
							Type:        horse.AbilityType(s.ColumnInt(44)),
							ValueUsage:  horse.AbilityValueUsage(s.ColumnInt(45)),
							Value:       horse.TenThousandths(s.ColumnInt(46)),
							Target:      horse.AbilityTarget(s.ColumnInt(47)),
							TargetValue: s.ColumnInt32(48),
						},
					}),
				},
			}),
			UniqueOwner: s.ColumnText(52), // TODO(zeph): should be id, not name
			Tags:        parseTags(s.ColumnText(54)),
			SPCost:      s.ColumnInt(49),
			IconID:      s.ColumnInt(53),
		}
	})
}

func parseTags(s string) []uint16 {
	r := make([]uint16, 0, 8)
	for s != "" {
		t, u, _ := strings.Cut(s, "/")
		s = u
		v, err := strconv.ParseUint(t, 10, 16)
		if err != nil {
			panic(fmt.Errorf("parsing skill tags: %w", err))
		}
		r = append(r, uint16(v))
	}
	return trimZeros(r...)
}

func trimAbilities(s []horse.Ability) []horse.Ability {
	for len(s) > 0 && s[len(s)-1].Type == 0 {
		s = s[:len(s)-1]
	}
	return s
}

func trimActivations(s []horse.Activation) []horse.Activation {
	for len(s) > 0 && s[len(s)-1].Condition == "" {
		s = s[:len(s)-1]
	}
	return s
}

func trimZeros[T comparable](s ...T) []T {
	var zero T
	for len(s) > 0 && s[len(s)-1] == zero {
		s = s[:len(s)-1]
	}
	return s
}
