package sema

import (
	"github.com/marzeq/qk/parser"
	"github.com/marzeq/qk/types"
)

type aliasState int

const (
	aliasUnseen aliasState = iota
	aliasResolving
	aliasResolved
)

type aliasInfo struct {
	node     *parser.TypeAliasNode
	state    aliasState
	resolved types.Type
}

func (a *Analyser) resolveAlias(info *aliasInfo, node parser.Node, indirect bool) types.Type {
	switch info.state {
	case aliasResolving:
		named, namedType := info.node.Type.(*parser.NamedTypeNode)
		genericApplication := namedType && len(named.TypeArguments) != 0
		if indirect || genericApplication {
			if _, ok := info.node.Type.(*parser.TraitTypeNode); ok && !info.node.Transparent {
				return types.TraitType{Module: a.currentMod, Name: info.node.Name}
			}
			return &types.AliasRef{Module: a.currentMod, Name: info.node.Name, Target: &info.resolved}
		}
		a.errorf(node, "circular type definition detected")
		return types.ErrorType{}

	case aliasResolved:
		return info.node.Symbol.TypeInfo
	}

	info.state = aliasResolving

	previousTraitContext := a.resolvingTraitMethodTypes
	a.resolvingTraitMethodTypes = false
	// Preserve the indirection that led into this named type. A recursive
	// reference may pass through intermediate aliases before returning to the
	// type whose storage is pointer-backed. Completeness validation still
	// rejects cycles that ultimately contain values inline.
	resolved := a.resolveTypeNodeAt(info.node.Type, indirect)
	a.resolvingTraitMethodTypes = previousTraitContext
	if trait, ok := resolved.(types.TraitType); ok && !info.node.Transparent {
		trait.Module = a.currentMod
		trait.Name = info.node.Name
		resolved = trait
		a.nominalTraits[trait.Module+":"+trait.Name] = trait
		a.registerTraitDefaults(info.node, trait, a.currentRoot)
	} else if !info.node.Transparent {
		resolved = types.DefinedType{Module: a.currentMod, Name: info.node.Name, Underlying: resolved}
		a.concreteTypes[a.currentMod+":"+info.node.Name] = resolved
	}

	info.node.Symbol.TypeInfo = resolved
	info.resolved = resolved
	info.state = aliasResolved

	return resolved
}