package infra

import (
	"fmt"
	"sort"
	"strings"
)

// Samba (LAN network-sharing) renderers — R-7 slice 1. PURE like the rest of this package: share
// list in, file contents out; no docker, no filesystem. The orchestration (availability filtering,
// write, compose up, smbpasswd) lives in internal/stacks/samba.go (ReconcileSamba). The transport +
// daemon set are the R-6 spike verdict (SPIKE-lan-discovery-2026-07-18): host network, smbd + nmbd +
// wsdd, `bind interfaces only` on lo+eth0.

// SambaShareRender is one exported folder as the renderer needs it (already availability-filtered by
// the caller — a dead-mount share is simply absent from the slice, never rendered).
type SambaShareRender struct {
	Name     string
	Path     string // absolute host path
	ReadOnly bool
}

// SambaData is the full input for both samba renderers.
type SambaData struct {
	ServerName string // NetBIOS name (validated by the settings layer)
	Shares     []SambaShareRender
	UID        int // household uid/gid the container runs shares as (1000)
}

// SambaHouseholdUser is the single household SMB account name (matches the entrypoint's unix user).
const SambaHouseholdUser = "felhom"

// SambaHostInterface is the guest's LAN interface, and the single source of truth for it: smb.conf's
// `interfaces =` line, the container's FELHOM_IFACE env, and the controller's LAN-address read
// (stacks.SambaLANAddress, v0.151.0) must all name the SAME nic or the service and the address the
// page prints drift apart.
const SambaHostInterface = "eth0"

// SambaContainerName is the fixed container name the compose render pins. It is THE single source of
// truth for it: the renderer below interpolates this constant, stacks.sambaContainer aliases it for
// the exec paths, monitor's effective-protected set watches it (R-7b liveness), and the backup
// package execs it for the passdb capture/restore. Anything that needs "which container is samba"
// reads this — never a second string literal.
const SambaContainerName = "felhom-samba"

// SambaPassdbVolume / SambaPassdbMount name the docker volume that holds the household SMB credential
// (the passdb tdb set) and where the container mounts it. R-7b's payload capture/restore targets this
// mount point, so it is pinned here beside the render that creates it rather than duplicated there.
const (
	SambaPassdbVolume = "samba-passdb"
	SambaPassdbMount  = "/var/lib/samba"
)

// RenderSambaConfig renders smb.conf: the hardened global block (bind interfaces only = lo eth0,
// SMB2+ floor, NetBIOS on for flat-name resolution) plus one [section] per share. Deterministic:
// shares are emitted in the given order (the caller preserves registry order). force user/group pin
// every written file to the household uid so apps and both backup tiers see consistent ownership.
func RenderSambaConfig(d SambaData) string {
	var b strings.Builder
	b.WriteString("# Samba (LAN network-sharing) — managed by felhom-controller (R-7).\n")
	b.WriteString("# WARNING: auto-generated. Manual edits are overwritten on the next share change.\n")
	b.WriteString("[global]\n")
	b.WriteString("  workgroup = WORKGROUP\n")
	b.WriteString("  server string = Felhom hálózati megosztás\n")
	fmt.Fprintf(&b, "  netbios name = %s\n", d.ServerName)
	b.WriteString("  security = user\n")
	b.WriteString("  map to guest = never\n")
	b.WriteString("  server min protocol = SMB2\n")
	b.WriteString("  disable netbios = no\n")
	b.WriteString("  bind interfaces only = yes\n")
	fmt.Fprintf(&b, "  interfaces = lo %s\n", SambaHostInterface)
	b.WriteString("  smb ports = 445\n")
	b.WriteString("  load printers = no\n")
	b.WriteString("  printing = bsd\n")
	b.WriteString("  printcap name = /dev/null\n")
	b.WriteString("  disable spoolss = yes\n")

	for _, sh := range d.Shares {
		ro := "no"
		if sh.ReadOnly {
			ro = "yes"
		}
		fmt.Fprintf(&b, "\n[%s]\n", sh.Name)
		fmt.Fprintf(&b, "  path = %s\n", sh.Path)
		fmt.Fprintf(&b, "  read only = %s\n", ro)
		fmt.Fprintf(&b, "  valid users = %s\n", SambaHouseholdUser)
		fmt.Fprintf(&b, "  force user = %s\n", SambaHouseholdUser)
		fmt.Fprintf(&b, "  force group = %s\n", SambaHouseholdUser)
		b.WriteString("  create mask = 0644\n")
		b.WriteString("  directory mask = 0755\n")
	}
	return b.String()
}

// RenderSambaCompose renders the samba stack's docker-compose.yml: host network (the spike mandate —
// the default bridge is deaf to LAN multicast), the pinned image, smb.conf bind-mounted read-only,
// the passdb named volume, and one bind per share (:ro for read-only shares — defense in depth beside
// the smb.conf-level `read only`). Deterministic: share binds are sorted so the output is stable.
func RenderSambaCompose(d SambaData) string {
	shares := make([]SambaShareRender, len(d.Shares))
	copy(shares, d.Shares)
	sort.Slice(shares, func(i, j int) bool { return shares[i].Path < shares[j].Path })

	var binds strings.Builder
	for _, sh := range shares {
		if sh.ReadOnly {
			fmt.Fprintf(&binds, "      - %s:%s:ro\n", sh.Path, sh.Path)
		} else {
			fmt.Fprintf(&binds, "      - %s:%s\n", sh.Path, sh.Path)
		}
	}

	return fmt.Sprintf(`# Samba (LAN network-sharing) — managed by felhom-controller (R-7 slice 1).
# WARNING: auto-generated. Manual edits are overwritten on the next share change.
# Host network is REQUIRED (SPIKE-lan-discovery-2026-07-18): the default docker bridge cannot
# receive the LAN multicast that WSD/mDNS discovery needs.

services:
  %[1]s:
    image: %[2]s
    container_name: %[1]s
    restart: unless-stopped
    network_mode: host
    environment:
      - FELHOM_SERVER_NAME=%[3]s
      - FELHOM_IFACE=%[8]s
      - FELHOM_UID=%[4]d
      - FELHOM_GID=%[4]d
    volumes:
      - ./smb.conf:/etc/samba/smb.conf:ro
      - %[5]s:%[6]s
%[7]s
volumes:
  %[5]s:
`, SambaContainerName, SambaImage, d.ServerName, d.UID, SambaPassdbVolume, SambaPassdbMount, binds.String(), SambaHostInterface)
}
