package bootstrap

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"path"

	"github.com/cloudreve/Cloudreve/v3/pkg/conf"
	"github.com/cloudreve/Cloudreve/v3/pkg/util"
	_ "github.com/cloudreve/Cloudreve/v3/statik"
	"github.com/gin-contrib/static"
	"github.com/rakyll/statik/fs"
)

const StaticFolder = "statics"

type GinFS struct {
	FS http.FileSystem
}

type staticVersion struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

// StaticFS 内置静态文件资源
var StaticFS static.ServeFileSystem

// Open 打开文件
func (b *GinFS) Open(name string) (http.File, error) {
	return b.FS.Open(name)
}

// Exists 文件是否存在
func (b *GinFS) Exists(prefix string, filepath string) bool {

	if _, err := b.FS.Open(filepath); err != nil {
		return false
	}
	return true

}

// InitStatic 初始化静态资源文件
func InitStatic() {
	var err error

	if util.Exists(util.RelativePath(StaticFolder)) {
		util.Log().Info("检测到 statics 目录存在,将使用此目录下的静态资源文件")
		StaticFS = static.LocalFile(util.RelativePath("statics"), false)

		// 检查静态资源的版本
		f, err := StaticFS.Open("version.json")
		if err != nil {
			util.Log().Warning("静态资源版本标识文件不存在,请重新构建或删除 statics 目录")
			return
		}

		b, err := ioutil.ReadAll(f)
		if err != nil {
			util.Log().Warning("无法读取静态资源文件版本,请重新构建或删除 statics 目录")
			return
		}

		var v staticVersion
		if err := json.Unmarshal(b, &v); err != nil {
			util.Log().Warning("无法解析静态资源文件版本, %s", err)
			return
		}

		staticName := "cloudreve-frontend"
		if conf.IsPro == "true" {
			staticName += "-pro"
		}

		if v.Name != staticName {
			util.Log().Warning("静态资源版本不匹配,请重新构建或删除 statics 目录")
			return
		}

		if v.Version != conf.RequiredStaticVersion {
			util.Log().Warning("静态资源版本不匹配 [当前 %s, 需要: %s],请重新构建或删除 statics 目录", v.Version, conf.RequiredStaticVersion)
			return
		}

	} else {
		StaticFS = &GinFS{}
		StaticFS.(*GinFS).FS, err = fs.New()
		if err != nil {
			util.Log().Panic("无法初始化静态资源, %s", err)
		}
	}

}

// Eject 抽离内置静态资源
func Eject() {
	staticFS, err := fs.New()
	if err != nil {
		util.Log().Panic("无法初始化静态资源, %s", err)
	}

	root, err := staticFS.Open("/")
	if err != nil {
		util.Log().Panic("根目录不存在, %s", err)
	}

	var walk func(relPath string, object http.File)
	walk = func(relPath string, object http.File) {
		stat, err := object.Stat()
		if err != nil {
			util.Log().Error("无法获取[%s]的信息, %s, 跳过...", relPath, err)
			return
		}

		if !stat.IsDir() {
			// 写入文件
			out, err := util.CreatNestedFile(util.RelativePath(StaticFolder + relPath))
			defer out.Close()

			if err != nil {
				util.Log().Error("无法创建文件[%s], %s, 跳过...", relPath, err)
				return
			}

			util.Log().Info("导出 [%s]...", relPath)
			if _, err := io.Copy(out, object); err != nil {
				util.Log().Error("无法写入文件[%s], %s, 跳过...", relPath, err)
				return
			}
		} else {
			// 列出目录
			objects, err := object.Readdir(0)
			if err != nil {
				util.Log().Error("无法步入子目录[%s], %s, 跳过...", relPath, err)
				return
			}

			// 递归遍历子目录
			for _, newObject := range objects {
				newPath := path.Join(relPath, newObject.Name())
				newRoot, err := staticFS.Open(newPath)
				if err != nil {
					util.Log().Error("无法打开对象[%s], %s, 跳过...", newPath, err)
					continue
				}
				walk(newPath, newRoot)
			}

		}
	}

	util.Log().Info("开始导出内置静态资源...")
	walk("/", root)
	util.Log().Info("内置静态资源导出完成")
}