package config

import (
	"fmt"
	"os"
	"path/filepath"

	webassets "ymhut-box/server/unified-management/web"
)

const defaultUpdateInfoJSON = `{
  "app_version": "0.0.0",
  "download_url": "",
  "update_notes": {},
  "last_update_notes": {},
  "release_notes": "",
  "release_notes_md": "",
  "last_updated": ""
}
`

const defaultMediaTypesJSON = `{
  "layout_version": "1.0.0",
  "last_updated": "",
  "ui_config": {},
  "categories": []
}
`

type Check struct {
	Name    string `json:"name"`
	Status  string `json:"status"`
	Path    string `json:"path,omitempty"`
	Message string `json:"message,omitempty"`
}

func Preflight(cfg *Config) []Check {
	checks := []Check{
		checkDir("storage", cfg.StorageDir, true),
		checkParent("sqlite", cfg.Database.SQLitePath),
		checkDir("update public", cfg.UpdatePublicDir, true),
		checkDir("update notice", cfg.UpdateNoticeDir, true),
		checkDir("downloads", cfg.DownloadsDir, true),
		checkSeedFile("legacy update-info", filepath.Join(cfg.UpdatePublicDir, "update-info.json"), []byte(defaultUpdateInfoJSON)),
		checkSeedFile("legacy media-types", filepath.Join(cfg.UpdatePublicDir, "media-types.json"), []byte(defaultMediaTypesJSON)),
		checkNoticeIndex("version notice index", filepath.Join(cfg.UpdateNoticeDir, "total.json")),
		checkWebBuild("admin web dist", cfg.AdminWebDir, "admin/dist"),
		checkWebBuild("portal web dist", cfg.PortalWebDir, "portal/dist"),
		checkWebBuild("setup web dist", cfg.SetupWebDir, "setup/dist"),
	}
	return checks
}

func checkDir(name, path string, create bool) Check {
	if create {
		if err := os.MkdirAll(path, 0o750); err != nil {
			return Check{Name: name, Status: "error", Path: path, Message: err.Error()}
		}
	}
	info, err := os.Stat(path)
	if err != nil {
		return Check{Name: name, Status: "missing", Path: path, Message: "directory not found"}
	}
	if !info.IsDir() {
		return Check{Name: name, Status: "error", Path: path, Message: "path is not a directory"}
	}
	return Check{Name: name, Status: "ok", Path: path}
}

func checkNoticeIndex(name, path string) Check {
	if _, err := os.Stat(path); err == nil {
		return Check{Name: name, Status: "ok", Path: path}
	} else if !os.IsNotExist(err) {
		return Check{Name: name, Status: "error", Path: path, Message: err.Error()}
	}
	if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
		return Check{Name: name, Status: "error", Path: path, Message: err.Error()}
	}
	data := []byte("{\n  \"schema_version\": 1,\n  \"product\": \"YMhut Box\",\n  \"versions\": []\n}\n")
	if err := os.WriteFile(path, data, 0o640); err != nil {
		return Check{Name: name, Status: "error", Path: path, Message: err.Error()}
	}
	return Check{Name: name, Status: "ok", Path: path, Message: "created empty notice index"}
}

func checkSeedFile(name, path string, data []byte) Check {
	if _, err := os.Stat(path); err == nil {
		return Check{Name: name, Status: "ok", Path: path}
	} else if !os.IsNotExist(err) {
		return Check{Name: name, Status: "error", Path: path, Message: err.Error()}
	}
	if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
		return Check{Name: name, Status: "error", Path: path, Message: err.Error()}
	}
	if err := os.WriteFile(path, data, 0o640); err != nil {
		return Check{Name: name, Status: "error", Path: path, Message: err.Error()}
	}
	return Check{Name: name, Status: "ok", Path: path, Message: "created default compatibility JSON"}
}

func checkParent(name, path string) Check {
	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0o750); err != nil {
		return Check{Name: name, Status: "error", Path: path, Message: err.Error()}
	}
	return Check{Name: name, Status: "ok", Path: path}
}

func checkFile(name, path string, required bool) Check {
	info, err := os.Stat(path)
	if err != nil {
		status := "missing"
		if required {
			status = "error"
		}
		return Check{Name: name, Status: status, Path: path, Message: "file not found"}
	}
	if info.IsDir() {
		return Check{Name: name, Status: "error", Path: path, Message: "path is a directory"}
	}
	return Check{Name: name, Status: "ok", Path: path}
}

func checkWebBuild(name, path, embedRoot string) Check {
	dir := checkDir(name, path, false)
	if dir.Status != "ok" {
		if embeddedWebBuildOK(embedRoot) {
			return Check{Name: name, Status: "ok", Path: path, Message: "using embedded frontend assets"}
		}
		dir.Message = "frontend dist missing; run npm install && npm run build, or build release with -tags embed_web"
		return dir
	}
	index := filepath.Join(path, "index.html")
	if file := checkFile(name+" index", index, true); file.Status != "ok" {
		if embeddedWebBuildOK(embedRoot) {
			return Check{Name: name, Status: "ok", Path: path, Message: "disk index missing; using embedded frontend assets"}
		}
		return Check{Name: name, Status: "missing", Path: index, Message: "index.html missing; run npm run build"}
	}
	assets := filepath.Join(path, "assets")
	if assetDir := checkDir(name+" assets", assets, false); assetDir.Status != "ok" {
		if embeddedWebBuildOK(embedRoot) {
			return Check{Name: name, Status: "ok", Path: path, Message: "disk assets missing; using embedded frontend assets"}
		}
		return Check{Name: name, Status: "missing", Path: assets, Message: "assets directory missing; run npm run build"}
	}
	return Check{Name: name, Status: "ok", Path: path}
}

func embeddedWebBuildOK(embedRoot string) bool {
	if !webassets.Embedded {
		return false
	}
	if _, err := webassets.ReadFile(filepath.ToSlash(filepath.Join(embedRoot, "index.html"))); err != nil {
		return false
	}
	entries, err := webassets.ReadDir(filepath.ToSlash(filepath.Join(embedRoot, "assets")))
	if err != nil {
		return false
	}
	for _, entry := range entries {
		if !entry.IsDir() {
			return true
		}
	}
	return false
}

func FormatPreflight(cfg *Config, checks []Check) []string {
	lines := make([]string, 0, len(checks))
	for _, check := range checks {
		line := fmt.Sprintf("[%s] %s", check.Status, check.Name)
		if check.Path != "" {
			line += " -> " + relativePath(cfg.BaseDir, check.Path)
		}
		if check.Message != "" {
			line += " (" + check.Message + ")"
		}
		lines = append(lines, line)
	}
	return lines
}
