package main

import (
	"errors"
	encrypt "fckeuspy-go/lib"
	"fmt"
	"image/color"
	"io"
	"os"
	"strings"
	"time"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/storage"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

type uiParts struct {
	outKey        *widget.Entry
	msg           *widget.Entry
	peer          *widget.Entry
	cipherOut     *widget.Entry
	payload       *widget.Entry
	plainOut      *widget.Entry
	toastLabel    *widget.Label
	cipherQR      *canvas.Image
	pubQR         *canvas.Image
	crtQR         *canvas.Image
	showQR        bool
	peerQR        *canvas.Image
	showPeerQR    bool
	payloadQR     *canvas.Image
	showPayloadQR bool
}

func buildEntries() *uiParts {
	p := &uiParts{
		outKey:        widget.NewMultiLineEntry(),
		msg:           widget.NewMultiLineEntry(),
		peer:          widget.NewMultiLineEntry(),
		cipherOut:     widget.NewMultiLineEntry(),
		payload:       widget.NewMultiLineEntry(),
		plainOut:      widget.NewMultiLineEntry(),
		toastLabel:    widget.NewLabel(""),
		cipherQR:      canvas.NewImageFromImage(nil),
		pubQR:         canvas.NewImageFromImage(nil),
		crtQR:         canvas.NewImageFromImage(nil),
		showQR:        true,
		peerQR:        canvas.NewImageFromImage(nil),
		showPeerQR:    true,
		payloadQR:     canvas.NewImageFromImage(nil),
		showPayloadQR: true,
	}
	p.cipherQR.FillMode = canvas.ImageFillContain
	p.cipherQR.SetMinSize(fyne.NewSize(220, 220))
	p.pubQR.FillMode = canvas.ImageFillContain
	p.pubQR.SetMinSize(fyne.NewSize(200, 200))
	p.crtQR.FillMode = canvas.ImageFillContain
	p.crtQR.SetMinSize(fyne.NewSize(200, 200))
	p.peerQR.FillMode = canvas.ImageFillContain
	p.peerQR.SetMinSize(fyne.NewSize(200, 200))
	p.payloadQR.FillMode = canvas.ImageFillContain
	p.payloadQR.SetMinSize(fyne.NewSize(220, 220))
	p.outKey.SetPlaceHolder("Veřejný klíč / certifikát…")
	p.msg.SetPlaceHolder("Sem napiš zprávu…")
	p.peer.SetPlaceHolder("-----BEGIN PUBLIC KEY----- … nebo CERTIFICATE …")
	p.cipherOut.SetPlaceHolder(`{"ek":"…","n":"…","ct":"…"}`)
	p.payload.SetPlaceHolder(`{"ek":"…","n":"…","ct":"…"}`)
	p.plainOut.SetPlaceHolder("Dešifrovaná zpráva…")
	// Zvýšení výšky (více řádků viditelně)
	p.outKey.SetMinRowsVisible(15)
	p.peer.SetMinRowsVisible(15)
	p.msg.SetMinRowsVisible(6)
	p.cipherOut.SetMinRowsVisible(15)
	p.payload.SetMinRowsVisible(15)
	p.plainOut.SetMinRowsVisible(15)
	p.toastLabel.Hide()
	return p
}

func (p *uiParts) showToast(msg string) {
	fyne.Do(func() {
		p.toastLabel.SetText(msg)
		p.toastLabel.Show()
	})
	time.AfterFunc(1500*time.Millisecond, func() {
		fyne.Do(func() {
			if p.toastLabel.Text == msg { // avoid race if overwritten
				p.toastLabel.SetText("")
				p.toastLabel.Hide()
			}
		})
	})
}

// custom fixed dark theme
type simpleTheme struct{}

func (simpleTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
	base := theme.DefaultTheme().Color(n, v)
	switch n {
	case theme.ColorNameBackground:
		return color.NRGBA{R: 24, G: 27, B: 31, A: 255}
	case theme.ColorNameButton:
		return color.NRGBA{R: 42, G: 46, B: 52, A: 255}
	case theme.ColorNameInputBackground:
		return color.NRGBA{R: 35, G: 39, B: 45, A: 255}
	case theme.ColorNameDisabled:
		// Zesvětlený disabled text pro lepší čitelnost read-only polí
		return color.NRGBA{R: 230, G: 233, B: 238, A: 255}
	}
	return base
}
func (simpleTheme) Font(st fyne.TextStyle) fyne.Resource    { return theme.DefaultTheme().Font(st) }
func (simpleTheme) Icon(n fyne.ThemeIconName) fyne.Resource { return theme.DefaultTheme().Icon(n) }
func (simpleTheme) Size(n fyne.ThemeSizeName) float32       { return theme.DefaultTheme().Size(n) }

var forceDark = true // can be toggled later

// Build key section
func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.CanvasObject {
	// Toolbar actions
	identityToggle := widget.NewToolbarAction(theme.VisibilityOffIcon(), nil)
	identityToolbar := widget.NewToolbar(
		widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(svc.PublicPEM()+"\n"+svc.PublicCert(), parts) }),
		identityToggle,
	)

	deleteBtn := widget.NewButton("Smazat identitu", func() {
		pwEntry := widget.NewPasswordEntry()
		pwEntry.SetPlaceHolder("Heslo pro potvrzení…")
		content := widget.NewForm(widget.NewFormItem("Heslo", pwEntry))
		dialog.ShowCustomConfirm("Potvrdit smazání", "Smazat", "Zrušit", content, func(ok bool) {
			if !ok {
				return
			}
			pw := pwEntry.Text
			if pw == "" {
				dialog.NewError(errors.New("heslo je prázdné"), fyne.CurrentApp().Driver().AllWindows()[0]).Show()
				return
			}
			if _, err := encrypt.OpenEncryptedStore(vaultPath, pw); err != nil {
				dialog.NewError(errors.New("neplatné heslo"), fyne.CurrentApp().Driver().AllWindows()[0]).Show()
				return
			}
			if err := os.Remove(vaultPath); err != nil {
				dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
				return
			}
			fyne.CurrentApp().Quit()
		}, fyne.CurrentApp().Driver().AllWindows()[0])
	})

	makeQR := func(data string, target *canvas.Image) {
		if data == "" {
			target.Image = nil
			target.Refresh()
			return
		}
		pngBytes, err := GenerateQRPNG(data, 512)
		if err != nil {
			return
		}
		img, err := LoadPNG(pngBytes)
		if err != nil {
			return
		}
		target.Image = img
		target.Refresh()
	}
	updateQRImages := func() {
		if parts.showQR {
			makeQR(svc.PublicPEM(), parts.pubQR)
			makeQR(svc.PublicCert(), parts.crtQR)
		} else {
			parts.pubQR.Image = nil
			parts.pubQR.Refresh()
			parts.crtQR.Image = nil
			parts.crtQR.Refresh()
		}
	}

	identityContainer := container.NewVBox()
	rebuild := func() {
		identityContainer.Objects = nil
		if parts.showQR {
			updateQRImages()
			// Wrap each QR with a small icon copy button
			pubBox := container.NewVBox(
				widget.NewLabel("Veřejný klíč (PEM)"),
				parts.pubQR,
				widget.NewButtonWithIcon("", theme.ContentCopyIcon(), func() { copyClip(svc.PublicPEM(), parts) }),
			)
			crtBox := container.NewVBox(
				widget.NewLabel("Certifikát (X.509)"),
				parts.crtQR,
				widget.NewButtonWithIcon("", theme.ContentCopyIcon(), func() { copyClip(svc.PublicCert(), parts) }),
			)
			identityContainer.Add(container.NewGridWithColumns(2, pubBox, crtBox))
		} else {
			// show combined text for convenience
			parts.outKey.SetText(svc.PublicPEM() + "\n" + svc.PublicCert())
			parts.outKey.Disable()
			identityContainer.Add(container.NewVBox(
				widget.NewLabel("Veřejný klíč + Certifikát (plaintext)"),
				parts.outKey,
			))
		}
		identityContainer.Refresh()
		if parts.showQR {
			identityToggle.SetIcon(theme.VisibilityOffIcon())
		} else {
			identityToggle.SetIcon(theme.VisibilityIcon())
		}
	}
	identityToggle.OnActivated = func() { parts.showQR = !parts.showQR; rebuild() }
	rebuild()

	// Header with toolbar and content + destructive action
	header := container.NewHBox(widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer(), identityToolbar)
	content := container.NewVBox(header, identityContainer, buttonTile(deleteBtn))
	return container.NewVScroll(content)
}

// Helper functions separated to avoid circular dependency with encrypt.Service
// We'll adapt by passing a small facade interface if needed.

type ServiceFacade interface {
	Encrypt(msg, peer string) (string, error)
	Decrypt(json string) (string, error)
	PublicPEM() string
	PublicCert() string
	// Contacts API (only implemented by VaultService)
	ListContacts() ([]Contact, error)
	SaveContact(c Contact) error
	DeleteContact(id string) error
}

func copyClip(s string, parts *uiParts) {
	fyne.CurrentApp().Clipboard().SetContent(s)
	parts.showToast("Zkopírováno")
}

// (Removed legacy buildEncryptSection and buildDecryptSection)

// assembleResponsive builds split view that collapses for narrow widths
// Tab: Encrypt
func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
	parts.cipherOut.Disable()

	// Peer section with QR/Text toggle
	peerContainer := container.NewVBox()
	peerToggleAction := widget.NewToolbarAction(theme.VisibilityOffIcon(), nil)
	var updatePeer func()
	updatePeerQR := func(text string) {
		if text == "" {
			parts.peerQR.Image = nil
			parts.peerQR.Refresh()
			return
		}
		pngBytes, err := GenerateQRPNG(text, 512)
		if err != nil {
			return
		}
		img, err := LoadPNG(pngBytes)
		if err != nil {
			return
		}
		parts.peerQR.Image = img
		parts.peerQR.Refresh()
	}
	updatePeer = func() {
		peerContainer.Objects = nil
		if parts.showPeerQR {
			updatePeerQR(parts.peer.Text)
			peerContainer.Add(parts.peerQR) // copy tlačítko jen v toolbaru
			peerToggleAction.SetIcon(theme.VisibilityOffIcon())
		} else {
			peerContainer.Add(parts.peer)
			peerToggleAction.SetIcon(theme.VisibilityIcon())
		}
		peerContainer.Refresh()
	}
	peerToggleAction.OnActivated = func() { parts.showPeerQR = !parts.showPeerQR; updatePeer() }
	parts.peer.OnChanged = func(string) {
		if parts.showPeerQR {
			updatePeerQR(parts.peer.Text)
		}
	}
	updatePeer()

	// Output section with QR/Text toggle
	outputContainer := container.NewVBox()
	outputToggleAction := widget.NewToolbarAction(theme.VisibilityOffIcon(), nil)
	updateQR := func(text string) {
		if text == "" {
			parts.cipherQR.Image = nil
			parts.cipherQR.Refresh()
			return
		}
		pngBytes, err := GenerateQRPNG(text, 512)
		if err != nil {
			parts.showToast("QR error")
			return
		}
		img, err := LoadPNG(pngBytes)
		if err != nil {
			parts.showToast("PNG err")
			return
		}
		parts.cipherQR.Image = img
		parts.cipherQR.Refresh()
	}
	updateMode := func() {
		outputContainer.Objects = nil
		if parts.showQR {
			updateQR(parts.cipherOut.Text)
			outputContainer.Add(parts.cipherQR) // copy tlačítko jen v toolbaru
			outputToggleAction.SetIcon(theme.VisibilityOffIcon())
		} else {
			outputContainer.Add(parts.cipherOut)
			outputToggleAction.SetIcon(theme.VisibilityIcon())
		}
		outputContainer.Refresh()
	}
	outputToggleAction.OnActivated = func() { parts.showQR = !parts.showQR; updateMode() }
	updateMode()

	encAction := func() {
		m := parts.msg.Text
		p := parts.peer.Text
		if m == "" || p == "" {
			parts.showToast("Chybí data")
			return
		}
		go func(msg, peer string) {
			res, err := svc.Encrypt(msg, peer)
			if err != nil {
				fyne.Do(func() { parts.cipherOut.SetText(""); updateQR(""); parts.showToast("Chyba") })
				return
			}
			fyne.Do(func() {
				parts.cipherOut.SetText(res)
				if parts.showQR {
					updateQR(res)
				}
				parts.showToast("OK")
			})
		}(m, p)
	}

	importPeerQR := func() {
		fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
			if err != nil || r == nil {
				return
			}
			defer r.Close()
			data, _ := io.ReadAll(r)
			img, err := LoadPNG(data)
			if err != nil {
				dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
				return
			}
			text, err := DecodeQR(img)
			if err != nil {
				dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
				return
			}
			parts.peer.SetText(text)
		}, fyne.CurrentApp().Driver().AllWindows()[0])
		fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"}))
		fd.Show()
	}

	// Toolbars
	peerToolbar := widget.NewToolbar(
		widget.NewToolbarAction(theme.ContentPasteIcon(), func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }),
		widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(parts.peer.Text, parts) }),
		widget.NewToolbarAction(theme.FolderOpenIcon(), importPeerQR),
		widget.NewToolbarAction(theme.ContentClearIcon(), func() { parts.peer.SetText("") }),
		widget.NewToolbarSeparator(),
		peerToggleAction,
	)
	outputToolbar := widget.NewToolbar(
		widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(parts.cipherOut.Text, parts) }),
		widget.NewToolbarAction(theme.ContentClearIcon(), func() {
			parts.cipherOut.SetText("")
			parts.msg.SetText("")
			if parts.showQR {
				updateQR("")
			}
		}),
		widget.NewToolbarSeparator(),
		outputToggleAction,
	)

	// Šifrování automaticky při změně zprávy nebo peer
	parts.msg.OnChanged = func(string) { encAction() }
	parts.peer.OnChanged = func(string) {
		if parts.showPeerQR {
			updatePeerQR(parts.peer.Text)
		}
		encAction()
	}

	// Sections
	peerSection := container.NewVBox(
		container.NewHBox(widget.NewLabel("Veřejný klíč příjemce"), layout.NewSpacer(), peerToolbar),
		peerContainer,
	)
	msgSection := container.NewVBox(
		widget.NewLabel("Zpráva"),
		parts.msg,
	)
	outputSection := container.NewVBox(
		container.NewHBox(widget.NewLabel("Výstup"), layout.NewSpacer(), outputToolbar),
		outputContainer,
	)
	group := container.NewVBox(
		widget.NewLabelWithStyle("Šifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
		peerSection,
		msgSection,
		outputSection,
	)
	return container.NewVScroll(group)
}

// Tab: Decrypt
func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
	parts.plainOut.Disable()
	decryptAction := func() {
		pl := parts.payload.Text
		if pl == "" {
			parts.plainOut.SetText("")
			return
		}
		go func(js string) {
			res, err := svc.Decrypt(js)
			if err != nil {
				fyne.Do(func() { parts.plainOut.SetText("") })
				return
			}
			fyne.Do(func() { parts.plainOut.SetText(res) })
		}(pl)
	}
	updatePayloadQR := func(text string) {
		if text == "" {
			parts.payloadQR.Image = nil
			parts.payloadQR.Refresh()
			return
		}
		pngBytes, err := GenerateQRPNG(text, 512)
		if err != nil {
			return
		}
		img, err := LoadPNG(pngBytes)
		if err != nil {
			return
		}
		parts.payloadQR.Image = img
		parts.payloadQR.Refresh()
	}
	importPayloadQR := func() {
		fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
			if err != nil || r == nil {
				return
			}
			defer r.Close()
			data, _ := io.ReadAll(r)
			img, err := LoadPNG(data)
			if err != nil {
				dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
				return
			}
			text, err := DecodeQR(img)
			if err != nil {
				dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
				return
			}
			parts.payload.SetText(text)
			if parts.showPayloadQR {
				updatePayloadQR(text)
			}
			decryptAction()
		}, fyne.CurrentApp().Driver().AllWindows()[0])
		fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"}))
		fd.Show()
	}
	// Toolbar toggle action for payload section
	payloadToggleAction := widget.NewToolbarAction(theme.VisibilityOffIcon(), nil)

	payloadContainer := container.NewVBox()
	updateMode := func() {
		payloadContainer.Objects = nil
		if parts.showPayloadQR {
			updatePayloadQR(parts.payload.Text)
			// Jen samotný QR bez spodního copy tlačítka
			payloadContainer.Add(parts.payloadQR)
			payloadToggleAction.SetIcon(theme.VisibilityOffIcon())
		} else {
			payloadContainer.Add(parts.payload)
			payloadToggleAction.SetIcon(theme.VisibilityIcon())
		}
		payloadContainer.Refresh()
	}
	payloadToggleAction.OnActivated = func() { parts.showPayloadQR = !parts.showPayloadQR; updateMode() }
	parts.payload.OnChanged = func(string) {
		if parts.showPayloadQR {
			updatePayloadQR(parts.payload.Text)
		}
		decryptAction()
	}
	updateMode()

	// Build payload toolbar
	payloadToolbar := widget.NewToolbar(
		widget.NewToolbarAction(theme.ContentPasteIcon(), func() {
			clip := fyne.CurrentApp().Clipboard().Content()
			parts.payload.SetText(clip)
		}),
		widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(parts.payload.Text, parts) }),
		widget.NewToolbarAction(theme.FolderOpenIcon(), importPayloadQR),
		widget.NewToolbarAction(theme.ContentClearIcon(), func() { parts.payload.SetText("") }),
		widget.NewToolbarSeparator(),
		payloadToggleAction,
	)

	payloadSection := container.NewVBox(
		container.NewHBox(widget.NewLabel("Payload"), layout.NewSpacer(), payloadToolbar),
		payloadContainer,
	)
	resultHeader := container.NewHBox(widget.NewLabel("Výsledek"), layout.NewSpacer())
	plainWrap := container.NewBorder(resultHeader, nil, nil, nil, parts.plainOut)
	split := container.NewVSplit(payloadSection, plainWrap)
	split.SetOffset(0.30)
	group := container.NewVBox(
		widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
		split,
	)
	return group
}

func buildTabbedUI(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.CanvasObject {
	idTab := container.NewTabItem("Identita", buildIdentityTab(parts, svc, vaultPath))
	contactsTab := container.NewTabItem("Kontakty", buildContactsTab(parts, svc))
	encTab := container.NewTabItem("Šifrování", buildEncryptTab(parts, svc))
	decTab := container.NewTabItem("Dešifrování", buildDecryptTab(parts, svc))
	tabs := container.NewAppTabs(idTab, contactsTab, encTab, decTab)
	tabs.SetTabLocation(container.TabLocationTop)

	// apply theme; allow toggle via hidden shortcut Ctrl+T (demo)
	fyne.CurrentApp().Settings().SetTheme(simpleTheme{})
	// register a shortcut to toggle QR/text globally (Ctrl+Q) and encrypt/decrypt (Ctrl+Enter inside tab)
	// (Shortcut toggle removed - API placeholder, lze doplnit přes desktop accelerator)
	prefs := fyne.CurrentApp().Preferences()
	// Vždy start na identitě (index 0), ignoruje předchozí uloženou pozici
	tabs.SelectIndex(0)
	tabs.OnSelected = func(ti *container.TabItem) {
		if prefs != nil {
			prefs.SetInt("lastTab", tabs.SelectedIndex())
		}
	}
	return container.NewBorder(nil, parts.toastLabel, nil, nil, tabs)
}

// buttonTile renders buttons above a related entry with a colored background spanning full width
func buttonTile(btns ...fyne.CanvasObject) fyne.CanvasObject {
	if len(btns) == 0 {
		return widget.NewLabel("")
	}
	cols := len(btns)
	if cols > 3 {
		cols = 3
	} // wrap into multiple rows if many
	grid := container.NewGridWithColumns(cols, btns...)
	bgColor := color.NRGBA{R: 240, G: 240, B: 245, A: 255}
	if forceDark {
		bgColor = color.NRGBA{R: 50, G: 54, B: 60, A: 255}
	}
	rect := canvas.NewRectangle(bgColor)
	rect.SetMinSize(fyne.NewSize(100, 38))
	padded := container.NewPadded(grid)
	return container.NewStack(rect, padded)
}

// --- Contacts UI ---

func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
	// Data + filtering
	var all []Contact
	var filtered []Contact
	selected := -1     // index into filtered (real contacts only, not counting draft)
	var draft *Contact // transient unsaved contact placeholder; not persisted until save
	load := func() {
		items, _ := svc.ListContacts()
		all = items
		filtered = items
	}
	applyFilter := func(q string) {
		if q == "" {
			filtered = all
			return
		}
		lower := strings.ToLower(q)
		tmp := make([]Contact, 0, len(all))
		for _, c := range all {
			if strings.Contains(strings.ToLower(c.Name), lower) || strings.Contains(strings.ToLower(c.Cert), lower) {
				tmp = append(tmp, c)
			}
		}
		filtered = tmp
	}

	// Left list (includes optional draft as first row)
	list := widget.NewList(
		func() int {
			if draft != nil {
				return len(filtered) + 1
			}
			return len(filtered)
		},
		func() fyne.CanvasObject { return widget.NewLabel("") },
		func(i widget.ListItemID, o fyne.CanvasObject) {
			if draft != nil {
				if int(i) == 0 { // draft row
					name := draft.Name
					if strings.TrimSpace(name) == "" {
						name = "(nový)"
					}
					o.(*widget.Label).SetText("✳ " + name + " (nový)")
					return
				}
				// shift index for real contacts
				real := int(i) - 1
				if real >= 0 && real < len(filtered) {
					name := filtered[real].Name
					if name == "" {
						name = "(bez názvu)"
					}
					o.(*widget.Label).SetText(name)
				}
				return
			}
			// no draft
			if i >= 0 && int(i) < len(filtered) {
				name := filtered[i].Name
				if name == "" {
					name = "(bez názvu)"
				}
				o.(*widget.Label).SetText(name)
			}
		},
	)

	noResults := widget.NewLabel("Žádné kontakty")
	noResults.Alignment = fyne.TextAlignCenter
	noResults.Hide()

	// searchQuery drží poslední text vyhledávání – empty state jen pokud není prázdný a nic nenalezeno
	searchQuery := ""
	updateEmptyState := func() {
		// pokud je draft aktivní, neukazovat prázdný stav
		if draft != nil {
			noResults.Hide()
			return
		}
		if len(filtered) == 0 && searchQuery != "" {
			noResults.Show()
		} else {
			noResults.Hide()
		}
	}

	// Right detail form
	nameEntry := widget.NewEntry()
	certEntry := widget.NewMultiLineEntry()
	certEntry.SetMinRowsVisible(8)
	certEntry.Wrapping = fyne.TextWrapWord
	// Detail toolbar
	certToolbar := widget.NewToolbar(
		widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(certEntry.Text, parts) }),
		widget.NewToolbarAction(theme.ContentPasteIcon(), func() {
			certEntry.SetText(fyne.CurrentApp().Clipboard().Content())
		}),
		widget.NewToolbarAction(theme.ContentClearIcon(), func() { certEntry.SetText("") }),
	)
	// helper pro generování default názvu
	makeDefaultName := func() string {
		base := "Nový kontakt"
		// pokud neexistuje žádný s přesným názvem, vrať jen base
		existsExact := false
		maxN := 1
		for _, c := range all {
			if c.Name == base {
				existsExact = true
			}
			if strings.HasPrefix(c.Name, base+" ") { // varianty s číslem
				var n int
				if _, err := fmt.Sscanf(c.Name, "Nový kontakt %d", &n); err == nil {
					if n >= maxN {
						maxN = n + 1
					}
				}
			}
		}
		if !existsExact {
			return base
		}
		return fmt.Sprintf("%s %d", base, maxN)
	}

	// search entry (musí být před saveBtn kvůli použití)
	search := widget.NewEntry()
	search.SetPlaceHolder("Hledat…")

	saveBtn := widget.NewButtonWithIcon("Uložit", theme.ConfirmIcon(), func() {
		name := strings.TrimSpace(nameEntry.Text)
		cert := strings.TrimSpace(certEntry.Text)
		if cert == "" {
			parts.showToast("Chybí cert")
			return
		}
		if name == "" || name == "Kontakt" || name == "Nový kontakt" {
			name = makeDefaultName()
		}
		if draft != nil { // saving draft as new contact
			_ = svc.SaveContact(Contact{Name: name, Cert: cert})
			draft = nil
			searchQuery = "" // clear filter to show new
			search.SetText("")
		} else if selected < 0 || selected >= len(filtered) { // new without draft (fallback)
			_ = svc.SaveContact(Contact{Name: name, Cert: cert})
			searchQuery = ""
			search.SetText("")
		} else { // update existing
			c := filtered[selected]
			c.Name = name
			c.Cert = cert
			_ = svc.SaveContact(c)
		}
		load()
		applyFilter(searchQuery)
		list.Refresh()
		updateEmptyState()
		parts.showToast("Uloženo")
	})
	useBtn := widget.NewButtonWithIcon("Použít ve Šifrování", theme.ConfirmIcon(), func() {
		parts.peer.SetText(certEntry.Text)
		parts.showToast("Kontakt vložen do Šifrování")
	})

	certRow := container.NewHBox(certToolbar, layout.NewSpacer(), saveBtn, useBtn)
	var detail fyne.CanvasObject
	detail = container.NewBorder(
		container.NewVBox(
			widget.NewLabel("Název"),
			nameEntry,
			widget.NewLabel("Certifikát / Public key"),
			certRow,
		),
		nil,
		nil, nil,
		certEntry,
	)

	// List selection behavior
	list.OnSelected = func(id widget.ListItemID) {
		idx := int(id)
		if draft != nil {
			if idx == 0 { // selecting draft row
				selected = -1
				nameEntry.SetText(draft.Name)
				certEntry.SetText(draft.Cert)
				return
			}
			idx = idx - 1 // shift for real contacts
		}
		if idx < 0 || idx >= len(filtered) {
			return
		}
		selected = idx
		// cancel draft if switching away
		if draft != nil {
			draft = nil
			list.Refresh()
		}
		c := filtered[idx]
		nameEntry.SetText(c.Name)
		certEntry.SetText(c.Cert)
	}
	list.OnUnselected = func(widget.ListItemID) { selected = -1 }

	// Header: title, search, toolbar
	search.OnChanged = func(s string) {
		searchQuery = s
		applyFilter(s)
		selected = -1
		// keep draft row if present; clear form only if no draft
		if draft == nil {
			nameEntry.SetText("")
			certEntry.SetText("")
		}
		list.Refresh()
		updateEmptyState()
	}
	addNewLabelBtn := widget.NewButtonWithIcon("Vytvořit nový", theme.ContentAddIcon(), func() {
		// If draft already exists just reselect it
		if draft != nil {
			list.Select(0)
			return
		}
		// remove selection of any existing contact
		selected = -1
		list.UnselectAll()
		// create draft placeholder
		draft = &Contact{ID: "__draft__", Name: makeDefaultName(), Cert: ""}
		nameEntry.SetText(draft.Name)
		certEntry.SetText("")
		// ensure filter cleared so user sees draft on top
		searchQuery = ""
		search.SetText("")
		list.Refresh()
		list.Select(0)
		updateEmptyState()
		parts.showToast("Nový kontakt (draft)")
	})
	// (Odstraněno tlačítko 'vložit z clipboardu' z horní lišty podle požadavku)
	addFromFile := widget.NewToolbarAction(theme.FolderOpenIcon(), func() {
		fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
			if err != nil || r == nil {
				return
			}
			defer r.Close()
			b, _ := io.ReadAll(r)
			txt := string(b)
			nm := extractCN(txt)
			if nm == "" {
				nm = "Nový kontakt"
			}
			nameEntry.SetText(nm)
			certEntry.SetText(txt)
			selected = -1
		}, fyne.CurrentApp().Driver().AllWindows()[0])
		fd.Show()
	})

	// Live update draft placeholder name as user types
	nameEntry.OnChanged = func(s string) {
		if draft != nil {
			draft.Name = s
			list.Refresh()
		}
	}
	// Delete button přesunuto k Uložit / Použít
	deleteBtn := widget.NewButtonWithIcon("", theme.DeleteIcon(), func() {
		// If draft active and no real contact chosen -> cancel draft
		if draft != nil && selected == -1 {
			draft = nil
			nameEntry.SetText("")
			certEntry.SetText("")
			list.Refresh()
			updateEmptyState()
			parts.showToast("Zrušeno")
			return
		}
		if selected < 0 || selected >= len(filtered) {
			return
		}
		c := filtered[selected]
		confirmMsg := fmt.Sprintf("Opravdu smazat kontakt %q?", c.Name)
		dialog.NewConfirm("Smazat kontakt", confirmMsg, func(ok bool) {
			if !ok {
				return
			}
			_ = svc.DeleteContact(c.ID)
			load()
			applyFilter(search.Text)
			selected = -1
			nameEntry.SetText("")
			certEntry.SetText("")
			list.Refresh()
			updateEmptyState()
			parts.showToast("Smazáno")
		}, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
	})
	// Přestavění certRow aby obsahoval i deleteBtn (nahrazení původního detailu níže)
	certRow = container.NewHBox(certToolbar, layout.NewSpacer(), saveBtn, useBtn, deleteBtn)
	// aktualizace detail panelu: nahradíme předchozí variantu
	detail = container.NewBorder(
		container.NewVBox(
			widget.NewLabel("Název"),
			nameEntry,
			widget.NewLabel("Certifikát / Public key"),
			certRow,
		),
		nil,
		nil, nil,
		certEntry,
	)
	// Horní toolbar nyní jen se souborem (import z file)
	topToolbar := widget.NewToolbar(addFromFile)
	header := container.NewHBox(widget.NewLabelWithStyle("Kontakty", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer(), addNewLabelBtn, topToolbar)

	// Left side: search fixed top, list fills remaining (widget.List scrolluje sama); overlay noResults
	left := container.NewBorder(search, nil, nil, nil, container.NewStack(list, noResults))
	right := container.NewStack(detail)
	split := container.NewHSplit(left, right)
	split.SetOffset(0.35)

	// Initialize
	load()
	updateEmptyState()
	list.Refresh()
	return container.NewBorder(header, nil, nil, nil, split)
}
