//go:build windows

// Package gui implements the Walk-based graphical interface for xetup.
//
// Three sequential phases, each with its own window:
//  1. Config form  – PC name, product key, profile, step checkboxes,
//                    load/save config buttons for per-client presets
//  2. Live run     – real-time log streamed from PowerShell scripts
//  3. Summary      – per-step OK / ERROR / SKIPPED with elapsed time
package gui

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/lxn/walk"
	. "github.com/lxn/walk/declarative"

	"git.xetup.x9.cz/x9/xetup/internal/config"
	"git.xetup.x9.cz/x9/xetup/internal/runner"
)

// Run opens the xetup window and blocks until the user closes it.
func Run(cfg config.Config, runCfg runner.RunConfig, cfgPath string) {
	for {
		res := formPhase(cfg, runCfg, cfgPath)
		switch res.action {
		case "quit":
			return
		case "reload":
			cfg = res.cfg
			cfgPath = res.cfgPath
		case "run":
			runCfg.ProfileType = res.cfg.Deployment.ProfileType
			results := runPhase(runCfg, res.steps)
			donePhase(results)
			return
		}
	}
}

// --------------------------------------------------------------------------
// Phase 1 – Config form
// --------------------------------------------------------------------------

type formResult struct {
	action  string // "run", "reload", "quit"
	cfg     config.Config
	cfgPath string
	steps   []runner.Step
}

func formPhase(cfg config.Config, runCfg runner.RunConfig, cfgPath string) formResult {
	var mw *walk.MainWindow
	result := formResult{action: "quit", cfgPath: cfgPath}

	var (
		pcNameLE     *walk.LineEdit
		pcDescLE     *walk.LineEdit
		productKeyLE *walk.LineEdit
		profileCB    *walk.ComboBox
	)

	items := runner.AllSelectableItems()
	checkPtrs := make([]*walk.CheckBox, len(items))
	checkWidgets := make([]Widget, len(items))
	for i, item := range items {
		i := i // capture for closure
		checkWidgets[i] = CheckBox{
			AssignTo: &checkPtrs[i],
			Text:     item.Label,
			Checked:  itemEnabled(cfg, item),
		}
	}

	collectCfg := func() config.Config {
		out := cfg
		out.Deployment.PCName = pcNameLE.Text()
		out.Deployment.PCDescription = pcDescLE.Text()
		out.Activation.ProductKey = productKeyLE.Text()
		profiles := []string{"default", "admin", "user"}
		if idx := profileCB.CurrentIndex(); idx >= 0 && idx < len(profiles) {
			out.Deployment.ProfileType = profiles[idx]
		}
		selected := make(map[string]bool, len(items))
		for i, item := range items {
			selected[item.Key] = checkPtrs[i].Checked()
		}
		_, features := buildStepsAndFeatures(selected)
		out.Features = features
		return out
	}

	if err := (MainWindow{
		AssignTo: &mw,
		Title:    "xetup \u2014 Windows deployment",
		Size:     Size{Width: 760, Height: 740},
		Layout:   VBox{},
		Children: []Widget{
			// ── Form fields ──────────────────────────────────────────────────
			Composite{
				Layout: Grid{Columns: 2},
				Children: []Widget{
					Label{Text: "PC jmeno:"},
					LineEdit{
						AssignTo:  &pcNameLE,
						Text:      cfg.Deployment.PCName,
						CueBanner: "napr. NB-KLIENT-01  (prazdne = neprejmenovat)",
					},
					Label{Text: "Popis PC:"},
					LineEdit{
						AssignTo:  &pcDescLE,
						Text:      cfg.Deployment.PCDescription,
						CueBanner: "napr. PC recepce",
					},
					Label{Text: "Product Key:"},
					LineEdit{
						AssignTo:  &productKeyLE,
						Text:      cfg.Activation.ProductKey,
						CueBanner: "prazdne = OA3 / GVLK fallback",
					},
					Label{Text: "Profil:"},
					ComboBox{
						AssignTo:     &profileCB,
						Model:        []string{"default", "admin", "user"},
						CurrentIndex: profileIndex(cfg.Deployment.ProfileType),
					},
				},
			},
			HSeparator{},
			// ── Step checkboxes ──────────────────────────────────────────────
			Label{Text: "Kroky a nastaveni (odskrtnete co nechcete spustit):"},
			ScrollView{
				MinSize:  Size{Height: 400},
				Layout:   VBox{MarginsZero: true},
				Children: checkWidgets,
			},
			HSeparator{},
			// ── Buttons ──────────────────────────────────────────────────────
			Composite{
				Layout: HBox{MarginsZero: true},
				Children: []Widget{
					PushButton{
						Text: "Nacist config...",
						OnClicked: func() {
							dlg := new(walk.FileDialog)
							dlg.Filter = "JSON soubory (*.json)|*.json|Vsechny soubory (*.*)|*.*"
							dlg.Title = "Nacist konfiguraci"
							ok, err := dlg.ShowOpen(mw)
							if err != nil || !ok {
								return
							}
							data, err := os.ReadFile(dlg.FilePath)
							if err != nil {
								walk.MsgBox(mw, "Chyba", err.Error(), walk.MsgBoxIconError)
								return
							}
							newCfg := config.DefaultConfig()
							if err := json.Unmarshal(data, &newCfg); err != nil {
								walk.MsgBox(mw, "Chyba", err.Error(), walk.MsgBoxIconError)
								return
							}
							result = formResult{
								action:  "reload",
								cfg:     newCfg,
								cfgPath: dlg.FilePath,
							}
							mw.Close()
						},
					},
					PushButton{
						Text: "Ulozit config...",
						OnClicked: func() {
							dlg := new(walk.FileDialog)
							dlg.Filter = "JSON soubory (*.json)|*.json|Vsechny soubory (*.*)|*.*"
							dlg.Title = "Ulozit konfiguraci"
							dlg.FilePath = "config.json"
							ok, err := dlg.ShowSave(mw)
							if err != nil || !ok {
								return
							}
							data, err := json.MarshalIndent(collectCfg(), "", "  ")
							if err != nil {
								walk.MsgBox(mw, "Chyba", err.Error(), walk.MsgBoxIconError)
								return
							}
							if err := os.WriteFile(dlg.FilePath, data, 0644); err != nil {
								walk.MsgBox(mw, "Chyba", err.Error(), walk.MsgBoxIconError)
							}
						},
					},
					HSpacer{},
					PushButton{
						Text: "  SPUSTIT  ",
						OnClicked: func() {
							finalCfg := collectCfg()
							selected := make(map[string]bool, len(items))
							for i, item := range items {
								selected[item.Key] = checkPtrs[i].Checked()
							}
							steps, features := buildStepsAndFeatures(selected)
							finalCfg.Features = features
							_ = config.Save(finalCfg, cfgPath)
							result = formResult{
								action:  "run",
								cfg:     finalCfg,
								cfgPath: cfgPath,
								steps:   steps,
							}
							mw.Close()
						},
					},
				},
			},
		},
	}.Create()); err != nil {
		walk.MsgBox(nil, "Chyba", fmt.Sprintf("Nelze vytvorit okno: %v", err), walk.MsgBoxIconError)
		return result
	}

	mw.Run()
	return result
}

// --------------------------------------------------------------------------
// Phase 2 – Live run view
// --------------------------------------------------------------------------

func runPhase(runCfg runner.RunConfig, steps []runner.Step) []runner.Result {
	var (
		mw        *walk.MainWindow
		statusLbl *walk.Label
		logTE     *walk.TextEdit
		cancelFn  context.CancelFunc
	)

	if err := (MainWindow{
		AssignTo: &mw,
		Title:    "xetup \u2014 probiha instalace",
		Size:     Size{Width: 760, Height: 740},
		Layout:   VBox{},
		Children: []Widget{
			Label{AssignTo: &statusLbl, Text: "Spoustim..."},
			HSeparator{},
			TextEdit{
				AssignTo: &logTE,
				ReadOnly: true,
				VScroll:  true,
				Font:     Font{Family: "Consolas", PointSize: 9},
				MinSize:  Size{Height: 590},
			},
			HSeparator{},
			Composite{
				Layout: HBox{MarginsZero: true},
				Children: []Widget{
					HSpacer{},
					PushButton{
						Text: "  ZASTAVIT  ",
						OnClicked: func() {
							if cancelFn != nil {
								cancelFn()
							}
						},
					},
				},
			},
		},
	}.Create()); err != nil {
		return nil
	}

	var (
		mu      sync.Mutex
		results []runner.Result
		done    = make(chan struct{})
	)

	ctx, cancel := context.WithCancel(context.Background())
	cancelFn = cancel

	// Cancel running scripts when user closes the window.
	mw.Closing().Attach(func(_ *bool, _ walk.CloseReason) {
		cancelFn()
	})

	r := runner.New(
		runCfg,
		func(l runner.LogLine) {
			mw.Synchronize(func() {
				logTE.AppendText(l.Text + "\r\n")
			})
		},
		func(res runner.Result) {
			mw.Synchronize(func() {
				statusLbl.SetText(fmt.Sprintf(
					"Krok %s \u2013 %s: %s", res.Step.Num, res.Step.Name, res.Status,
				))
			})
		},
	)

	go func() {
		defer close(done)
		res := r.Run(ctx, steps)
		mu.Lock()
		results = res
		mu.Unlock()
		mw.Synchronize(func() {
			mw.Close()
		})
	}()

	mw.Run()
	cancel()  // stop scripts if window was closed by user
	<-done    // wait for goroutine to exit cleanly

	mu.Lock()
	defer mu.Unlock()
	return results
}

// --------------------------------------------------------------------------
// Phase 3 – Summary
// --------------------------------------------------------------------------

func donePhase(results []runner.Result) {
	var mw *walk.MainWindow

	ok, errs, skipped := 0, 0, 0
	rows := make([]Widget, 0, len(results))

	for _, res := range results {
		var icon string
		switch res.Status {
		case "OK":
			ok++
			icon = "OK    "
		case "ERROR":
			errs++
			icon = "ERR   "
		default:
			skipped++
			icon = "\u2013     "
		}
		text := icon + res.Step.Num + " \u2013 " + res.Step.Name
		if res.Elapsed > 0 {
			text += fmt.Sprintf("  (%s)", res.Elapsed.Round(time.Second))
		}
		rows = append(rows, Label{
			Text: text,
			Font: Font{Family: "Consolas", PointSize: 9},
		})
	}

	summaryText := fmt.Sprintf("OK: %d   CHYBY: %d   PRESKOCENO: %d", ok, errs, skipped)

	if err := (MainWindow{
		AssignTo: &mw,
		Title:    "xetup \u2014 hotovo",
		Size:     Size{Width: 760, Height: 740},
		Layout:   VBox{},
		Children: []Widget{
			Label{
				Text:      "Hotovo",
				Alignment: AlignHCenterVNear,
				Font:      Font{Bold: true, PointSize: 12},
			},
			HSeparator{},
			ScrollView{
				MinSize:  Size{Height: 560},
				Layout:   VBox{MarginsZero: true},
				Children: rows,
			},
			HSeparator{},
			Label{
				Text:      summaryText,
				Alignment: AlignHCenterVNear,
				Font:      Font{Bold: true},
			},
			Composite{
				Layout: HBox{MarginsZero: true},
				Children: []Widget{
					HSpacer{},
					PushButton{
						Text: "  ZAVRIT  ",
						OnClicked: func() {
							mw.Close()
						},
					},
					HSpacer{},
				},
			},
		},
	}.Create()); err != nil {
		return
	}

	mw.Run()
}

// --------------------------------------------------------------------------
// Helpers
// --------------------------------------------------------------------------

func itemEnabled(cfg config.Config, item runner.SelectableItem) bool {
	if item.FeatureID == "" {
		if v, ok := cfg.Steps[item.StepID]; ok {
			return v
		}
		return true
	}
	if feats, ok := cfg.Features[item.StepID]; ok {
		if v, ok2 := feats[item.FeatureID]; ok2 {
			return v
		}
	}
	return true
}

func buildStepsAndFeatures(selected map[string]bool) ([]runner.Step, config.Features) {
	items    := runner.AllSelectableItems()
	features := make(config.Features)
	stepOn   := make(map[string]bool)

	for _, item := range items {
		if item.FeatureID == "" {
			stepOn[item.StepID] = selected[item.Key]
		} else {
			if features[item.StepID] == nil {
				features[item.StepID] = make(map[string]bool)
			}
			features[item.StepID][item.FeatureID] = selected[item.Key]
			if selected[item.Key] {
				stepOn[item.StepID] = true
			}
		}
	}

	allSteps := runner.AllSteps()
	steps := make([]runner.Step, len(allSteps))
	for i, s := range allSteps {
		s.Enabled = stepOn[s.ID]
		steps[i] = s
	}
	return steps, features
}

func profileIndex(p string) int {
	switch p {
	case "admin":
		return 1
	case "user":
		return 2
	default:
		return 0
	}
}
