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

package source

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

type Dir struct {
	Path string `yaml:"path" json:"path"`
	subDirsStack []string `yaml:"-" json:"-"`
}

func NewDir() *Dir {
	return &Dir{
		subDirsStack: make([]string, 0, 100),
	}
}

func init() {
	SourceTypes.Register([]string{"file"}, func(u *url.URL) DocSource {
		t := NewDir()
		t.Path,_ = filepath.Abs(filepath.Join(u.Hostname(), u.RequestURI()))
		return t
	})

}


func (d *Dir) Type() string { return "dir" }

func (d *Dir) ExtractDirectory(path string) (*resource.Document, error) {
	document := resource.NewDocument()
	files, err := os.ReadDir(path)
	if err != nil {
		return nil, err
	}

	for _,file := range files {
		f := resource.NewFile()
		f.Path = filepath.Join(path, file.Name())
		info, infoErr := file.Info()
		if infoErr != nil {
			return document, infoErr
		}

		if fiErr := f.UpdateAttributesFromFileInfo(info); fiErr != nil {
			return document, fiErr
		}

		f.FileType.SetMode(file.Type())

		if file.IsDir() {
			d.subDirsStack = append(d.subDirsStack, f.Path)
		} else {
			fileReader, fileReaderErr := os.Open(f.Path)
			if fileReaderErr != nil {
				return document, fileReaderErr
			}

			readFileData, readErr := io.ReadAll(fileReader)
			if readErr != nil {
				return document, readErr
			}
			f.Content = string(readFileData)
		}

		document.AddResourceDeclaration("file", f)
	}
	return document, nil
}

func (d *Dir) ExtractResources(filter ResourceSelector) ([]*resource.Document, error) {
	documents := make([]*resource.Document, 0, 100)

	d.subDirsStack = append(d.subDirsStack, d.Path)

	for {
		if len(d.subDirsStack) == 0 {
			break
		}
		var dirPath string
		dirPath, d.subDirsStack = d.subDirsStack[len(d.subDirsStack) - 1], d.subDirsStack[:len(d.subDirsStack) - 1]
		document, dirErr := d.ExtractDirectory(dirPath)
		if dirErr != nil {
			return documents, dirErr
		}
		
		documents = append(documents, document)
	}
	return documents, nil
}
