// SPDX-License-Identifier: GPL-3.0-or-later

//go:build linux

package service

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
)

// openrcManager implements Manager for OpenRC (Alpine, Gentoo, etc.).
// OpenRC only supports system-level services (requires root).
type openrcManager struct{}

const openrcScriptTemplate = `#!/sbin/openrc-run
# Private Archive Client Backend

name="{{.Name}}"
description="{{.Description}}"
command="{{.BinaryPath}}"
{{- if .Args}}
command_args="{{.Args}}"
{{- end}}
command_background="yes"
pidfile="/run/${RC_SVCNAME}.pid"
start_stop_daemon_args="--make-pidfile"

{{- if .User}}
command_user="{{.User}}"
{{- end}}

{{- if .WorkingDirectory}}
directory="{{.WorkingDirectory}}"
{{- end}}

output_log="/var/log/privatebackup/backend.log"
error_log="/var/log/privatebackup/backend-error.log"

depend() {
	need net
	after firewall
}

start_pre() {
	checkpath --directory --owner {{.User}}:{{.User}} --mode 0755 /var/log/privatebackup
}
`

type openrcTemplateData struct {
	Name             string
	Description      string
	BinaryPath       string
	Args             string
	User             string
	WorkingDirectory string
}

func (m *openrcManager) InitSystem() InitSystem {
	return InitOpenRC
}

// scriptPath returns the path to the OpenRC init script.
func (m *openrcManager) scriptPath() string {
	return filepath.Join("/etc/init.d", ServiceName)
}

func (m *openrcManager) IsInstalled() bool {
	_, err := os.Stat(m.scriptPath())
	return err == nil
}

func (m *openrcManager) Install(cfg Config) error {
	if m.IsInstalled() {
		return ErrServiceAlreadyInstalled
	}

	// OpenRC requires root for system-level services.
	if !isRoot() {
		return ErrInsufficientPrivileges
	}

	// Force system-level for OpenRC (no user-level concept).
	cfg.Level = LevelSystem

	user := cfg.User
	if user == "" {
		user = currentUsername()
	}

	data := openrcTemplateData{
		Name:             ServiceName,
		Description:      cfg.Description,
		BinaryPath:       cfg.BinaryPath,
		Args:             strings.Join(cfg.Args, " "),
		User:             user,
		WorkingDirectory: cfg.WorkingDirectory,
	}

	tmpl, err := template.New("openrc").Parse(openrcScriptTemplate)
	if err != nil {
		return fmt.Errorf("failed to parse init script template: %w", err)
	}

	f, err := os.OpenFile(m.scriptPath(), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
	if err != nil {
		return fmt.Errorf("failed to create init script: %w", err)
	}
	defer f.Close()

	if err := tmpl.Execute(f, data); err != nil {
		return fmt.Errorf("failed to render init script: %w", err)
	}

	// Add to default runlevel.
	cmd := exec.Command("rc-update", "add", ServiceName, "default")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to add service to default runlevel: %w", err)
	}

	return nil
}

func (m *openrcManager) Uninstall() error {
	if !m.IsInstalled() {
		return ErrServiceNotInstalled
	}

	// Stop first.
	_ = m.Stop()

	// Remove from runlevels.
	_ = exec.Command("rc-update", "del", ServiceName).Run()

	// Remove init script.
	if err := os.Remove(m.scriptPath()); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("failed to remove init script: %w", err)
	}

	return nil
}

func (m *openrcManager) Start() error {
	if !m.IsInstalled() {
		return ErrServiceNotInstalled
	}
	cmd := exec.Command("rc-service", ServiceName, "start")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func (m *openrcManager) Stop() error {
	if !m.IsInstalled() {
		return ErrServiceNotInstalled
	}
	cmd := exec.Command("rc-service", ServiceName, "stop")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func (m *openrcManager) Restart() error {
	if !m.IsInstalled() {
		return ErrServiceNotInstalled
	}
	cmd := exec.Command("rc-service", ServiceName, "restart")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func (m *openrcManager) Status() (Status, error) {
	st := Status{
		InitSystem: InitOpenRC,
		Level:      "system",
		UnitName:   ServiceName,
	}

	if !m.IsInstalled() {
		return st, nil
	}
	st.Installed = true

	// Check if running.
	out, err := exec.Command("rc-service", ServiceName, "status").Output()
	if err == nil && strings.Contains(string(out), "started") {
		st.Running = true
	}

	// Check if enabled (in default runlevel).
	out, err = exec.Command("rc-update", "show", "default").Output()
	if err == nil && strings.Contains(string(out), ServiceName) {
		st.Enabled = true
	}

	// Try to get PID from pidfile.
	pidData, err := os.ReadFile(fmt.Sprintf("/run/%s.pid", ServiceName))
	if err == nil {
		var pid int
		if _, err := fmt.Sscanf(string(pidData), "%d", &pid); err == nil && pid > 0 {
			st.PID = pid
		}
	}

	return st, nil
}
