// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.

package resource

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"gopkg.in/yaml.v3"
	"log/slog"
	"gitea.rosskeen.house/rosskeen.house/machine"
)

type DeclarationType struct {
	Type TypeName `json:"type" yaml:"type"`
	Transition string `json:"transition,omitempty" yaml:"transition,omitempty"`
}

type Declaration struct {
	StateMatchine machine.Stater `json:"-" yaml:"-"`
	Type TypeName `json:"type" yaml:"type"`
	Transition string `json:"transition,omitempty" yaml:"transition,omitempty"`
	Attributes Resource `json:"attributes" yaml:"attributes"`
}

type ResourceLoader interface {
	LoadDecl(string) error
}

type StateTransformer interface {
	Apply() error
}

func NewDeclaration() *Declaration {
	return &Declaration{}
}

func (d *Declaration) Clone() *Declaration {
	return &Declaration {
		Type: d.Type,
		Attributes: d.Attributes.Clone(),
	}
}

func (d *Declaration) Load(r io.Reader) error {
	c := NewYAMLDecoder(r)
	return c.Decode(d)
}

func (d *Declaration) LoadDecl(yamlResourceDeclaration string) error {
	c := NewYAMLStringDecoder(yamlResourceDeclaration)
	return c.Decode(d)
}

func (d *Declaration) NewResource() error {
	uri := fmt.Sprintf("%s://", d.Type)
	newResource, err := ResourceTypes.New(uri)
	d.Attributes = newResource
	return err
}

func (d *Declaration) Resource() Resource {
	return d.Attributes
}

func (d *Declaration) Apply() error {
	stater := d.Attributes.StateMachine()
	return stater.Trigger(d.Transition)
}

func (d *Declaration) SetURI(uri string) error {
	slog.Info("Declaration.SetURI()", "uri", uri, "declaration", d)
	d.Attributes = NewResource(uri)
	if d.Attributes == nil {
		panic("unknown resource")
	}
	d.Type = TypeName(d.Attributes.Type())
	_,e := d.Attributes.Read(context.Background()) // fix context
	return e
}

func (d *Declaration) UnmarshalYAML(value *yaml.Node) error {
	t := &DeclarationType{}
	if unmarshalResourceTypeErr := value.Decode(t); unmarshalResourceTypeErr != nil {
		return unmarshalResourceTypeErr
	}

	d.Type = t.Type
	d.Transition = t.Transition
	newResource, resourceErr := ResourceTypes.New(fmt.Sprintf("%s://", d.Type))
	if resourceErr != nil {
		return resourceErr
	}
	d.Attributes = newResource
	resourceAttrs := struct {
		Attributes yaml.Node `json:"attributes"`
	}{}
	if unmarshalAttributesErr := value.Decode(&resourceAttrs); unmarshalAttributesErr != nil {
		return unmarshalAttributesErr
	}
	if unmarshalResourceErr := resourceAttrs.Attributes.Decode(d.Attributes); unmarshalResourceErr != nil {
		return unmarshalResourceErr
	}
	return nil
}

func (d *Declaration) UnmarshalJSON(data []byte) error {
	t := &DeclarationType{}
	if unmarshalResourceTypeErr := json.Unmarshal(data, t); unmarshalResourceTypeErr != nil {
		return unmarshalResourceTypeErr
	}

	d.Type = t.Type
	d.Transition = t.Transition

	newResource, resourceErr := ResourceTypes.New(fmt.Sprintf("%s://", d.Type))
	if resourceErr != nil {
		return resourceErr
	}
	d.Attributes = newResource

	resourceAttrs := struct {
		Attributes Resource `json:"attributes"`
	}{Attributes: newResource}
	if unmarshalAttributesErr := json.Unmarshal(data, &resourceAttrs); unmarshalAttributesErr != nil {
		return unmarshalAttributesErr
	}

	return nil
}

/*
func (d *Declaration) MarshalJSON() ([]byte, error) {
  buf := new(bytes.Buffer)
  buf.WriteByte('"')
  buf.WriteString("value"))
  buf.WriteByte('"')
  return buf.Bytes(), nil
}
*/

func (d *Declaration) MarshalYAML() (any, error) {
	return d, nil
}
