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

package target

import (
_	"context"
_	"encoding/json"
_	"fmt"
_	"gopkg.in/yaml.v3"
	"net/url"
	"path/filepath"
	"decl/internal/resource"
	"os"
	"compress/gzip"
	"io"
_	"errors"
	"log/slog"
)

const (
	FormatYaml = "yaml"
	FormatJson = "json"
)

type DeclFile struct {
	Path string `yaml:"path" json:"path"`
	Gzip bool `yaml:"gzip,omitempty" json:"gzip,omitempty"`
	Format string `yaml:"format,omitempty" json:"format,omitempty"`
	encoder resource.Encoder `yaml:"-" json:"-"`
}

func NewDeclFile() *DeclFile {
	return &DeclFile{ Gzip: false }
}

func NewFileDocTarget(u *url.URL, format string, gzip bool, fileUri bool) DocTarget {
	t := NewDeclFile()
	t.Format = format
	t.Gzip = gzip
	if fileUri {
		fileAbsolutePath, _ := filepath.Abs(filepath.Join(u.Hostname(), u.RequestURI()))
		t.Path = fileAbsolutePath
	} else {
		t.Path = filepath.Join(u.Hostname(), u.Path)
	}
	return t
}

func init() {
	TargetTypes.Register([]string{"decl", "file"}, func(u *url.URL) DocTarget {
		t := NewDeclFile()
		if u.Path != "-" {
			t.Path,_ = filepath.Abs(filepath.Join(u.Hostname(), u.Path))
		} else {
			t.Path = "-"
		}
		if _,ok := u.Query()["gzip"]; ok {
			t.Gzip = true
		}
		if format,ok := u.Query()["format"]; ok {
			switch format[0] {
			case string(FormatYaml):
				t.Format = FormatYaml
			case string(FormatJson):
				t.Format = FormatJson
			}
		}
		return t
	})

	TargetTypes.Register([]string{"yaml.gz","yml.gz"}, func(u *url.URL) DocTarget {
		switch u.Scheme {
		case "yaml", "yml", "file":
			return NewFileDocTarget(u, FormatYaml, true, false)
		}
		return NewFileDocTarget(u, FormatYaml, true, false)
	})

	TargetTypes.Register([]string{"json.gz"}, func(u *url.URL) DocTarget {
		switch u.Scheme {
		case "json", "file":
			return NewFileDocTarget(u, FormatJson, true, false)
		}
		return NewFileDocTarget(u, FormatJson, true, false)
	})

	TargetTypes.Register([]string{"yaml","yml"}, func(u *url.URL) DocTarget {
		switch u.Scheme {
		case "yaml", "yml", "file":
			return NewFileDocTarget(u, FormatYaml, false, false)
		}
		return NewFileDocTarget(u, FormatYaml, false, false)
	})

	TargetTypes.Register([]string{"json"}, func(u *url.URL) DocTarget {
		switch u.Scheme {
		case "json", "file":
			return NewFileDocTarget(u, FormatJson, false, false)
		}
		return NewFileDocTarget(u, FormatJson, false, false)
	})

}


func (d *DeclFile) Type() string { return "decl" }

func (d *DeclFile) EmitResources(documents []*resource.Document, filter resource.ResourceSelector) (error) {
	var file *os.File
	var fileErr error
	var fileWriter io.Writer
	if d.Path == "" || d.Path == "-" {
		file = os.Stdout
	} else {
		file, fileErr = os.Open(d.Path)
		if fileErr != nil {
			return fileErr
		}
		defer func() {
				file.Close()
		}()
	}

	if d.Gzip {
		fileWriter = gzip.NewWriter(file)
	} else {
		fileWriter = file
	}

	switch d.Format {
	case FormatJson:
		d.encoder = resource.NewJSONEncoder(fileWriter)
	case FormatYaml:
		fallthrough
	default:
		d.encoder = resource.NewYAMLEncoder(fileWriter)
	}

	for _, doc := range documents {
		emitDoc := resource.NewDocument()
		if validationErr := doc.Validate(); validationErr != nil {
			return validationErr
		}
		for _, declaration := range doc.Filter(filter) {
			emitDoc.ResourceDecls = append(emitDoc.ResourceDecls, *declaration)
		}
		slog.Info("EmitResources", "doctarget", d, "encoder", d.encoder, "emit", emitDoc)
		if documentErr := d.encoder.Encode(emitDoc); documentErr != nil {
			slog.Info("EmitResources", "err", documentErr)
			return documentErr
		}
	}
	return nil
}
