package iscsi

import (
    "context"
    "fmt"
    "time"

    "github.com/example/storage-appliance/internal/infra/osexec"
)

// Adapter wraps targetcli invocations for LIO (targetcli) management.
type Adapter struct {
    Runner osexec.Runner
}

func NewAdapter(runner osexec.Runner) *Adapter { return &Adapter{Runner: runner} }

// CreateTarget creates an IQN target via targetcli
func (a *Adapter) CreateTarget(ctx context.Context, iqn string) error {
    // Use a short timeout for cli interactions
    ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
    defer cancel()
    _, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", "/iscsi", "create", iqn)
    if err != nil {
        return fmt.Errorf("targetcli create target failed: %v %s", err, stderr)
    }
    if code != 0 {
        return fmt.Errorf("targetcli create returned: %s", stderr)
    }
    return nil
}

// CreateBackstore creates a block backstore for a zvol device.
func (a *Adapter) CreateBackstore(ctx context.Context, name, devpath string) error {
    ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
    defer cancel()
    // targetcli syntax: /backstores/block create <name> <devpath>
    _, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", "/backstores/block", "create", name, devpath)
    if err != nil {
        return fmt.Errorf("targetcli create backstore failed: %v %s", err, stderr)
    }
    if code != 0 {
        return fmt.Errorf("targetcli backstore returned: %s", stderr)
    }
    return nil
}

// CreateLUN maps backstore into target's TPG1 LUNs
func (a *Adapter) CreateLUN(ctx context.Context, iqn, backstoreName string, lunID int) error {
    ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
    defer cancel()
    bsPath := fmt.Sprintf("/backstores/block/%s", backstoreName)
    tpgPath := fmt.Sprintf("/iscsi/%s/tpg1/luns", iqn)
    _, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", tpgPath, "create", bsPath)
    if err != nil {
        return fmt.Errorf("targetcli create lun failed: %v %s", err, stderr)
    }
    if code != 0 {
        return fmt.Errorf("targetcli create lun returned: %s", stderr)
    }
    return nil
}

// DeleteLUN unmaps a LUN from a target; if the LUN is mapped fail unless forced.
func (a *Adapter) DeleteLUN(ctx context.Context, iqn string, lunID int) error {
    ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
    defer cancel()
    tpgPath := fmt.Sprintf("/iscsi/%s/tpg1/luns", iqn)
    // delete by numeric id
    _, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", tpgPath, "delete", fmt.Sprintf("%d", lunID))
    if err != nil {
        return fmt.Errorf("targetcli delete lun failed: %v %s", err, stderr)
    }
    if code != 0 {
        return fmt.Errorf("targetcli delete lun returned: %s", stderr)
    }
    return nil
}

func (a *Adapter) AddPortal(ctx context.Context, iqn, address string, port int) error {
    ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
    defer cancel()
    tpgPath := fmt.Sprintf("/iscsi/%s/tpg1/portals", iqn)
    addr := fmt.Sprintf("%s:%d", address, port)
    _, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", tpgPath, "create", addr)
    if err != nil {
        return fmt.Errorf("targetcli add portal failed: %v %s", err, stderr)
    }
    if code != 0 {
        return fmt.Errorf("targetcli add portal returned: %s", stderr)
    }
    return nil
}

func (a *Adapter) AddACL(ctx context.Context, iqn, initiator string) error {
    ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
    defer cancel()
    aclPath := fmt.Sprintf("/iscsi/%s/tpg1/acls", iqn)
    _, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", aclPath, "create", initiator)
    if err != nil {
        return fmt.Errorf("targetcli add acl failed: %v %s", err, stderr)
    }
    if code != 0 {
        return fmt.Errorf("targetcli add acl returned: %s", stderr)
    }
    return nil
}

// Save writes the configuration to storage (saving targetcli config)
func (a *Adapter) Save(ctx context.Context) error {
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()
    _, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", "saveconfig")
    if err != nil {
        return fmt.Errorf("targetcli save failed: %v %s", err, stderr)
    }
    if code != 0 {
        return fmt.Errorf("targetcli save returned: %s", stderr)
    }
    return nil
}
