package main

import (
	encrypt "fckeuspy-go/lib"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/widget"
)

// strengthRowLayout rozdělí dostupnou šířku v poměru ~70:30 (bar+label : tlačítko)
type strengthRowLayout struct{}

func (l *strengthRowLayout) Layout(objs []fyne.CanvasObject, size fyne.Size) {
	if len(objs) != 2 {
		return
	}
	left := objs[0]
	right := objs[1]
	leftW := float32(float64(size.Width) * 0.70)
	if leftW < 10 {
		leftW = size.Width / 2
	}
	left.Resize(fyne.NewSize(leftW, size.Height))
	left.Move(fyne.NewPos(0, 0))
	right.Resize(fyne.NewSize(size.Width-leftW, size.Height))
	right.Move(fyne.NewPos(leftW, 0))
}

func (l *strengthRowLayout) MinSize(objs []fyne.CanvasObject) fyne.Size {
	var w, h float32
	if len(objs) == 2 {
		m1 := objs[0].MinSize()
		m2 := objs[1].MinSize()
		w = m1.Width + m2.Width
		if m1.Height > m2.Height {
			h = m1.Height
		} else {
			h = m2.Height
		}
	} else {
		for _, o := range objs {
			ms := o.MinSize()
			w += ms.Width
			if ms.Height > h {
				h = ms.Height
			}
		}
	}
	return fyne.NewSize(w, h)
}

func NewUI() (stprageDir string, window fyne.Window) {
	// App + storage dir
	a := app.NewWithID("fckeuspy")
	w := a.NewWindow("Encryptor (Fyne)")
	prefs := a.Preferences()
	width := prefs.IntWithFallback("winW", 1100)
	height := prefs.IntWithFallback("winH", 720)
	w.Resize(fyne.NewSize(float32(width), float32(height)))
	w.SetOnClosed(func() {
		sz := w.Canvas().Size()
		prefs.SetInt("winW", int(sz.Width))
		prefs.SetInt("winH", int(sz.Height))
	})

	// docasny bypass
	/*
		base, err := os.UserConfigDir()
		if err != nil {
			base, _ = os.UserHomeDir()
		}
	*/
	base := "./"

	//return filepath.Join(base, "fckeuspy-go"), w
	return filepath.Join(base, ""), w
}

// ShowPasswordVaultDialog zobrazí moderní odemykací / registrační dialog s countdown animací při špatném hesle.
// Callback je zavolán pouze při úspěchu (ne při neplatném hesle). Zrušení vrací password="".
func ShowPasswordVaultDialog(w fyne.Window, vaultPath string, onResult func(create bool, password string, commonName string)) {
	modeCreate := false
	if _, err := os.Stat(vaultPath); err != nil {
		modeCreate = true
	}
	statusLabel := widget.NewLabel("")
	statusLabel.Wrapping = fyne.TextWrapWord
	statusLabel.Hide()
	// Unlock
	unlockPw := widget.NewPasswordEntry()
	unlockPw.SetPlaceHolder("Heslo…")
	unlockBtn := widget.NewButton("Odemknout", nil)
	unlockForm := container.NewVBox(widget.NewForm(widget.NewFormItem("Heslo", unlockPw)), unlockBtn)
	// Create
	createPw1 := widget.NewPasswordEntry()
	createPw1.SetPlaceHolder("Heslo…")
	createPw2 := widget.NewPasswordEntry()
	createPw2.SetPlaceHolder("Znovu heslo…")
	commonNameEntry := widget.NewEntry()
	commonNameEntry.SetPlaceHolder("Jméno identity (např. Alice)…")
	strengthBar := widget.NewProgressBar()
	strengthBar.Min = 0
	strengthBar.Max = 100
	// Skryj defaultní procenta uvnitř progress baru, protože máme vlastní overlay label
	strengthBar.TextFormatter = func() string { return "" }
	strengthLabel := widget.NewLabel("")
	updateStrength := func(pw string) {
		s, d := passwordStrengthScore(pw)
		strengthBar.SetValue(float64(s))
		strengthLabel.SetText(fmt.Sprintf("%s:  %d%%", d, s))
	}
	createPw1.OnChanged = updateStrength
	genBtn := widget.NewButton("Generovat", func() {
		if v, err := encrypt.GenerateRandomPassword(24); err == nil {
			createPw1.SetText(v)
			createPw2.SetText(v)
			updateStrength(v)
		}
	})
	createBtn := widget.NewButton("Vytvořit trezor", nil)

	// Obě pole stejné šířky – žádné tlačítko uvnitř prvního řádku
	form := widget.NewForm(
		widget.NewFormItem("Jméno", commonNameEntry),
		widget.NewFormItem("Heslo", createPw1),
		widget.NewFormItem("Potvrzení", createPw2),
	)

	// Řádek: progress bar s textem uvnitř (overlay) : tlačítko (70:30)
	strengthLabel.Alignment = fyne.TextAlignCenter
	leftStack := container.NewStack(
		strengthBar,
		container.NewCenter(strengthLabel),
	)
	strengthRow := container.New(&strengthRowLayout{}, leftStack, genBtn)
	createForm := container.NewVBox(
		form,
		strengthRow,
		createBtn,
	)
	stack := container.NewStack(unlockForm, createForm)
	showMode := func(c bool) {
		modeCreate = c
		if c {
			unlockForm.Hide()
			createForm.Show()
		} else {
			createForm.Hide()
			unlockForm.Show()
		}
		statusLabel.Hide()
	}
	showMode(modeCreate)
	segment := widget.NewRadioGroup([]string{"Odemknout", "Vytvořit"}, func(val string) { showMode(val == "Vytvořit") })
	segment.Horizontal = true
	segment.Refresh()
	if modeCreate {
		segment.SetSelected("Vytvořit")
	} else {
		segment.SetSelected("Odemknout")
	}
	header := container.NewVBox(
		widget.NewLabelWithStyle("🔐 Trezor", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
		segment,
		widget.NewSeparator(),
	)
	var d dialog.Dialog
	completed := false
	var countdownCancel chan struct{}
	startCountdown := func(sec int) {
		if countdownCancel != nil {
			close(countdownCancel)
		}
		countdownCancel = make(chan struct{})
		statusLabel.Show()
		unlockBtn.Disable()
		unlockPw.Disable()
		go func() {
			for i := sec; i > 0; i-- {
				select {
				case <-countdownCancel:
					return
				default:
				}
				iLocal := i
				fyne.Do(func() { statusLabel.SetText(fmt.Sprintf("Neplatné heslo. Nový pokus za %d s", iLocal)) })
				time.Sleep(time.Second)
			}
			fyne.Do(func() { unlockBtn.Enable(); unlockPw.Enable(); unlockPw.SetText(""); statusLabel.Hide() })
		}()
	}
	unlockBtn.OnTapped = func() {
		pw := unlockPw.Text
		if pw == "" {
			statusLabel.SetText("Zadejte heslo")
			statusLabel.Show()
			return
		}
		if _, err := encrypt.OpenEncryptedStore(vaultPath, pw); err != nil {
			statusLabel.SetText("Neplatné heslo")
			statusLabel.Show()
			startCountdown(3)
			return
		}
		completed = true
		onResult(false, pw, "")
		d.Hide()
	}
	createBtn.OnTapped = func() {
		pw1, pw2 := createPw1.Text, createPw2.Text
		if pw1 != pw2 {
			statusLabel.SetText("Hesla se neshodují")
			statusLabel.Show()
			return
		}
		if err := encrypt.ValidatePasswordForUI(pw1); err != nil {
			statusLabel.SetText(err.Error())
			statusLabel.Show()
			return
		}
		completed = true
		cn := strings.TrimSpace(commonNameEntry.Text)
		onResult(true, pw1, cn)
		d.Hide()
	}
	body := container.NewVBox(header, container.NewPadded(stack), statusLabel)
	d = dialog.NewCustom("", "Zrušit", body, w)
	d.SetOnClosed(func() {
		if countdownCancel != nil {
			close(countdownCancel)
		}
		if !completed {
			onResult(false, "", "")
		}
	})
	d.Resize(fyne.NewSize(480, 320))
	d.Show()
	if modeCreate {
		w.Canvas().Focus(createPw1)
	} else {
		w.Canvas().Focus(unlockPw)
	}
}

// passwordStrengthScore: heuristický odhad síly (0-100) s ohledem na entropii, třídy znaků a penalizace.
func passwordStrengthScore(pw string) (int, string) {
	if pw == "" {
		return 0, "Prázdné"
	}
	length := len(pw)
	// Charakteristiky
	hasUpper, hasLower, hasDigit, hasSymbol := false, false, false, false
	symbols := "!@#$%^&*()_-+=[]{}/?:.,<>|~`'\""
	freq := make(map[rune]int)
	for _, r := range pw {
		freq[r]++
		switch {
		case r >= 'A' && r <= 'Z':
			hasUpper = true
		case r >= 'a' && r <= 'z':
			hasLower = true
		case r >= '0' && r <= '9':
			hasDigit = true
		default:
			if strings.ContainsRune(symbols, r) {
				hasSymbol = true
			}
		}
	}
	classes := 0
	if hasUpper {
		classes++
	}
	if hasLower {
		classes++
	}
	if hasDigit {
		classes++
	}
	if hasSymbol {
		classes++
	}

	// Shannon entropie per char
	var shannon float64
	for _, c := range freq {
		p := float64(c) / float64(length)
		shannon += -p * (log2(p))
	}
	// log2 hrubě implementujeme konverzí přes Ln, zde jednoduchá aproximace: log2(p) ~ (ln p)/0.693
	// aby nebyla složitost, definujeme pomocnou funkci nahoře - zjednodušeno níže (inline hack):
	// Použijeme předpočítané p^ využitím math (neimportujeme math -> krátký lookup) => nahradíme vlastní small table -> zvolíme fallback.
	// Kvůli jednoduchosti: pokud shannon > 4.5 považuj za 4.5 (limit pro běžná hesla)
	if shannon > 4.5 {
		shannon = 4.5
	}

	// Skóre komponenty
	lengthScore := 0
	switch {
	case length >= 20:
		lengthScore = 32
	case length >= 16:
		lengthScore = 26
	case length >= 12:
		lengthScore = 22
	case length >= 10:
		lengthScore = 18
	case length >= 8:
		lengthScore = 14
	case length >= 6:
		lengthScore = 8
	default:
		lengthScore = 4
	}
	classScore := classes * 10                // max 40
	entropyScore := int((shannon / 4.5) * 28) // max ~28

	// Penalizace monotónnosti & nízké diverzity
	uniqueChars := len(freq)
	diversityPenalty := 0
	if uniqueChars <= 2 {
		diversityPenalty = 25
	} else if uniqueChars <= 4 {
		diversityPenalty = 15
	} else if uniqueChars <= 6 {
		diversityPenalty = 5
	}
	// Sekvenční vzory
	lower := strings.ToLower(pw)
	for _, pat := range []string{"abc", "abcd", "qwert", "1234", "password", "heslo"} {
		if strings.Contains(lower, pat) {
			diversityPenalty += 10
		}
	}

	raw := lengthScore + classScore + entropyScore - diversityPenalty
	if raw < 0 {
		raw = 0
	}
	if raw > 100 {
		raw = 100
	}

	desc := "Slabé"
	switch {
	case raw >= 85:
		desc = "Silné"
	case raw >= 65:
		desc = "Dobré"
	case raw >= 45:
		desc = "Střední"
	case raw >= 25:
		desc = "Nízké"
	}
	return raw, desc
}

// log2 pomocná aproximace (rychlá, bez importu math)
func log2(x float64) float64 {
	// racionální aproximace: log2(x) ≈ ln(x)/ln(2); použijeme jednoduchý Newton pro ln
	if x <= 0 {
		return 0
	}
	// Polynomická aproximace pro ln v okolí 1: ln(x) ~ 2*( (y) + y^3/3 ), y=(x-1)/(x+1) (rychlý log)
	y := (x - 1) / (x + 1)
	y2 := y * y
	ln := 2 * (y + y2*y/3)
	return ln / 0.69314718056
}
