package store

import (
	"database/sql"
	"errors"
	"fmt"
	"strings"

	"github.com/google/uuid"
)

// SetWorkloadVolume upserts a volume mount keyed by (workload_id, target).
// The target is the natural key — re-using a target replaces the row
// rather than accumulating duplicates that would conflict at mount time.
func (s *Store) SetWorkloadVolume(v WorkloadVolume) (WorkloadVolume, error) {
	if v.WorkloadID == "" || v.Target == "" {
		return WorkloadVolume{}, fmt.Errorf("workload_volume: workload_id and target are required")
	}
	if v.Scope == "" {
		v.Scope = string(VolumeScopeAbsolute)
	}
	if !IsValidVolumeScope(v.Scope) {
		return WorkloadVolume{}, fmt.Errorf("workload_volume: invalid scope %q", v.Scope)
	}
	if v.ID == "" {
		v.ID = uuid.New().String()
	}
	now := Now()
	if _, err := s.db.Exec(
		`INSERT INTO workload_volumes (id, workload_id, source, target, scope, name, created_at, updated_at)
		 VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		 ON CONFLICT(workload_id, target) DO UPDATE SET
			source = excluded.source,
			scope = excluded.scope,
			name = excluded.name,
			updated_at = excluded.updated_at`,
		v.ID, v.WorkloadID, v.Source, v.Target, v.Scope, v.Name, now, now,
	); err != nil {
		return WorkloadVolume{}, fmt.Errorf("upsert workload volume: %w", err)
	}
	return s.getWorkloadVolumeByTarget(v.WorkloadID, v.Target)
}

// ListWorkloadVolumes returns every mount for the given workload, ordered
// by target so the UI rendering is stable across requests.
func (s *Store) ListWorkloadVolumes(workloadID string) ([]WorkloadVolume, error) {
	rows, err := s.db.Query(
		`SELECT id, workload_id, source, target, scope, name, created_at, updated_at
		 FROM workload_volumes WHERE workload_id = ? ORDER BY target`, workloadID,
	)
	if err != nil {
		return nil, fmt.Errorf("query workload volumes: %w", err)
	}
	defer rows.Close()
	out := []WorkloadVolume{}
	for rows.Next() {
		v, err := scanWorkloadVolume(rows)
		if err != nil {
			return nil, err
		}
		out = append(out, v)
	}
	return out, rows.Err()
}

// DeleteWorkloadVolume removes one mount by ID.
func (s *Store) DeleteWorkloadVolume(id string) error {
	result, err := s.db.Exec(`DELETE FROM workload_volumes WHERE id = ?`, id)
	if err != nil {
		return fmt.Errorf("delete workload volume: %w", err)
	}
	n, _ := result.RowsAffected()
	if n == 0 {
		return fmt.Errorf("workload volume %s: %w", id, ErrNotFound)
	}
	return nil
}

func (s *Store) getWorkloadVolumeByTarget(workloadID, target string) (WorkloadVolume, error) {
	var v WorkloadVolume
	err := s.db.QueryRow(
		`SELECT id, workload_id, source, target, scope, name, created_at, updated_at
		 FROM workload_volumes WHERE workload_id = ? AND target = ?`, workloadID, target,
	).Scan(&v.ID, &v.WorkloadID, &v.Source, &v.Target, &v.Scope, &v.Name, &v.CreatedAt, &v.UpdatedAt)
	if errors.Is(err, sql.ErrNoRows) {
		return WorkloadVolume{}, fmt.Errorf("workload volume (%s,%s): %w", workloadID, target, ErrNotFound)
	}
	if err != nil {
		return WorkloadVolume{}, fmt.Errorf("query workload volume: %w", err)
	}
	return v, nil
}

func scanWorkloadVolume(rows *sql.Rows) (WorkloadVolume, error) {
	var v WorkloadVolume
	if err := rows.Scan(&v.ID, &v.WorkloadID, &v.Source, &v.Target, &v.Scope, &v.Name,
		&v.CreatedAt, &v.UpdatedAt); err != nil {
		return WorkloadVolume{}, fmt.Errorf("scan workload volume: %w", err)
	}
	return v, nil
}

// normalizeAbsolutePath is a defensive helper for volume source paths in
// "absolute" scope. Rejects path-traversal segments so a malicious client
// can't escape an allow-listed prefix at the API layer. The actual
// allowed-paths check lives in settings.AllowedVolumePaths and remains
// the policy authority.
func normalizeAbsolutePath(p string) string {
	p = strings.TrimSpace(p)
	if p == "" {
		return ""
	}
	if strings.Contains(p, "..") {
		return ""
	}
	return p
}
