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

// Package client provides API methods for the backend.
package client

import (
	"context"
	"fmt"
	"time"
)

// API paths
const (
	pathHealth          = "/api/v1/health"
	pathStatus          = "/api/v1/status"
	pathStatusTor       = "/api/v1/status/tor"
	pathStatusServer    = "/api/v1/status/server"
	pathAccount         = "/api/v1/account"
	pathAccountRegister = "/api/v1/account/register"
	pathBackupStart     = "/api/v1/backup/start"
	pathBackupStatus    = "/api/v1/backup/status"
	pathBackupCancel    = "/api/v1/backup/cancel"
	pathRestoreStart    = "/api/v1/restore/start"
	pathRestoreStatus   = "/api/v1/restore/status"
	pathRestoreCancel   = "/api/v1/restore/cancel"
	pathSnapshots       = "/api/v1/snapshots"
	pathJobs            = "/api/v1/jobs"
	pathSchedules       = "/api/v1/schedules"
	pathConfig          = "/api/v1/config"
	pathMode            = "/api/v1/mode"
	pathPaths           = "/api/v1/paths"
	pathRetention       = "/api/v1/retention"
	pathCredentials     = "/api/v1/credentials"
	pathCredRotate      = "/api/v1/credentials/rotate"
	pathUpdatesStatus   = "/api/v1/updates/status"
	pathUpdatesCheck    = "/api/v1/updates/check"
	pathUpdatesConfig   = "/api/v1/updates/config"
	pathUpdatesBinary   = "/api/v1/updates/binary"
)

// Response types

// HealthResponse represents a health check response.
type HealthResponse struct {
	Status    string    `json:"status"`
	Timestamp time.Time `json:"timestamp"`
}

// StatusResponse represents the overall status response.
type StatusResponse struct {
	Status    string    `json:"status"`
	Version   string    `json:"version"`
	Uptime    string    `json:"uptime"`
	Timestamp time.Time `json:"timestamp"`
}

// TorStatusResponse represents Tor connection status.
type TorStatusResponse struct {
	Connected    bool      `json:"connected"`
	CircuitCount int       `json:"circuit_count"`
	BootstrapPct int       `json:"bootstrap_pct"`
	LastActivity time.Time `json:"last_activity"`
}

// ServerStatusResponse represents server connection status.
type ServerStatusResponse struct {
	Connected        bool      `json:"connected"`
	LastContactAt    time.Time `json:"last_contact_at"`
	AccountStatus    string    `json:"account_status"`
	CreditsRemaining float64   `json:"credits_remaining"`
}

// AccountResponse represents account information.
type AccountResponse struct {
	CertHash         string    `json:"cert_hash"`
	RegisteredAt     time.Time `json:"registered_at"`
	CreditsRemaining float64   `json:"credits_remaining"`
	StorageUsedBytes int64     `json:"storage_used_bytes"`
	Status           string    `json:"status"`
}

// BackupRequest represents a backup start request.
type BackupRequest struct {
	Paths   []string          `json:"paths"`
	Exclude []string          `json:"exclude,omitempty"`
	Tags    map[string]string `json:"tags,omitempty"`
}

// BackupStatusResponse represents backup status.
type BackupStatusResponse struct {
	JobID           string    `json:"job_id"`
	Status          string    `json:"status"`
	Progress        int       `json:"progress"`
	BytesProcessed  int64     `json:"bytes_processed"`
	FilesProcessed  int64     `json:"files_processed"`
	StartedAt       time.Time `json:"started_at,omitempty"`
	EstimatedTimeMs int64     `json:"estimated_time_ms,omitempty"`
	Error           string    `json:"error,omitempty"`
}

// RestoreRequest represents a restore start request.
type RestoreRequest struct {
	SnapshotID string   `json:"snapshot_id"`
	Target     string   `json:"target"`
	Paths      []string `json:"paths,omitempty"`
}

// RestoreStatusResponse represents restore status.
type RestoreStatusResponse struct {
	JobID           string    `json:"job_id"`
	Status          string    `json:"status"`
	Progress        int       `json:"progress"`
	BytesRestored   int64     `json:"bytes_restored"`
	FilesRestored   int64     `json:"files_restored"`
	StartedAt       time.Time `json:"started_at,omitempty"`
	EstimatedTimeMs int64     `json:"estimated_time_ms,omitempty"`
	Error           string    `json:"error,omitempty"`
}

// SnapshotResponse represents a snapshot.
type SnapshotResponse struct {
	ID       string            `json:"id"`
	Time     time.Time         `json:"time"`
	Hostname string            `json:"hostname"`
	Paths    []string          `json:"paths"`
	Tags     map[string]string `json:"tags,omitempty"`
	Size     int64             `json:"size"`
}

// JobResponse represents a job.
type JobResponse struct {
	ID          string                 `json:"id"`
	Type        string                 `json:"type"`
	Status      string                 `json:"status"`
	Progress    int                    `json:"progress"`
	CreatedAt   time.Time              `json:"created_at"`
	StartedAt   time.Time              `json:"started_at,omitempty"`
	CompletedAt time.Time              `json:"completed_at,omitempty"`
	Error       string                 `json:"error,omitempty"`
	Metadata    map[string]interface{} `json:"metadata,omitempty"`
}

// ScheduleRequest represents a schedule create/update request.
type ScheduleRequest struct {
	Type     string                 `json:"type"`
	Interval string                 `json:"interval"`
	Enabled  bool                   `json:"enabled"`
	Metadata map[string]interface{} `json:"metadata,omitempty"`
}

// ScheduleResponse represents a schedule.
type ScheduleResponse struct {
	Name     string                 `json:"name"`
	Type     string                 `json:"type"`
	Interval string                 `json:"interval"`
	Enabled  bool                   `json:"enabled"`
	LastRun  time.Time              `json:"last_run,omitempty"`
	NextRun  time.Time              `json:"next_run,omitempty"`
	Metadata map[string]interface{} `json:"metadata,omitempty"`
}

// RegisterRequest represents a registration request.
type RegisterRequest struct {
	ServerAddress string `json:"server_address"`
}

// ConfigResponse represents the configuration.
type ConfigResponse struct {
	BackupPaths         []string `json:"backup_paths"`
	ExcludePatterns     []string `json:"exclude_patterns"`
	ServerAddress       string   `json:"server_address"`
	TorEnabled          bool     `json:"tor_enabled"`
	Mode                string   `json:"mode"`
	AutoPrune           bool     `json:"auto_prune"`
	MissingPathBehavior string   `json:"missing_path_behavior"`
}

// ConfigUpdateRequest represents a configuration update request.
type ConfigUpdateRequest struct {
	BackupPaths         []string `json:"backup_paths,omitempty"`
	ExcludePatterns     []string `json:"exclude_patterns,omitempty"`
	MissingPathBehavior string   `json:"missing_path_behavior,omitempty"`
}

// ModeResponse represents the current mode.
type ModeResponse struct {
	Mode        string   `json:"mode"`
	Description string   `json:"description"`
	Features    []string `json:"features"`
}

// ModeUpdateRequest represents a mode change request.
type ModeUpdateRequest struct {
	Mode        string `json:"mode"`
	Acknowledge bool   `json:"acknowledge"`
}

// PathsResponse represents the configured backup paths.
type PathsResponse struct {
	Paths      []PathInfo `json:"paths"`
	TotalPaths int        `json:"total_paths"`
}

// PathInfo represents information about a single backup path.
type PathInfo struct {
	Path        string `json:"path"`
	Exists      bool   `json:"exists"`
	IsDir       bool   `json:"is_dir"`
	SizeBytes   int64  `json:"size_bytes,omitempty"`
	FileCount   int64  `json:"file_count,omitempty"`
	AccessError string `json:"access_error,omitempty"`
}

// PathAddRequest represents a request to add a backup path.
type PathAddRequest struct {
	Path string `json:"path"`
}

// PathRemoveRequest represents a request to remove a backup path.
type PathRemoveRequest struct {
	Path string `json:"path"`
}

// RetentionResponse represents the current retention policy.
type RetentionResponse struct {
	KeepWithinDaily   string `json:"keep_within_daily"`
	KeepWithinWeekly  string `json:"keep_within_weekly"`
	KeepWithinMonthly string `json:"keep_within_monthly"`
	KeepWithinYearly  string `json:"keep_within_yearly"`
	Description       string `json:"description"`
}

// API methods

// Health checks if the backend is healthy.
func (c *Client) Health(ctx context.Context) (*HealthResponse, error) {
	var resp HealthResponse
	if err := c.get(ctx, pathHealth, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// Status returns the backend status.
func (c *Client) Status(ctx context.Context) (*StatusResponse, error) {
	var resp StatusResponse
	if err := c.get(ctx, pathStatus, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// TorStatus returns Tor connection status.
func (c *Client) TorStatus(ctx context.Context) (*TorStatusResponse, error) {
	var resp TorStatusResponse
	if err := c.get(ctx, pathStatusTor, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// ServerStatus returns server connection status.
func (c *Client) ServerStatus(ctx context.Context) (*ServerStatusResponse, error) {
	var resp ServerStatusResponse
	if err := c.get(ctx, pathStatusServer, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// Account returns account information.
func (c *Client) Account(ctx context.Context) (*AccountResponse, error) {
	var resp AccountResponse
	if err := c.get(ctx, pathAccount, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// Register registers a new account.
func (c *Client) Register(ctx context.Context, req *RegisterRequest) (*AccountResponse, error) {
	var resp AccountResponse
	if err := c.post(ctx, pathAccountRegister, req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// StartBackup starts a backup operation.
func (c *Client) StartBackup(ctx context.Context, req *BackupRequest) (*BackupStatusResponse, error) {
	var resp BackupStatusResponse
	if err := c.post(ctx, pathBackupStart, req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// BackupStatus returns the current backup status.
func (c *Client) BackupStatus(ctx context.Context) (*BackupStatusResponse, error) {
	var resp BackupStatusResponse
	if err := c.get(ctx, pathBackupStatus, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// CancelBackup cancels the current backup.
func (c *Client) CancelBackup(ctx context.Context) error {
	var resp struct{}
	return c.post(ctx, pathBackupCancel, nil, &resp)
}

// StartRestore starts a restore operation.
func (c *Client) StartRestore(ctx context.Context, req *RestoreRequest) (*RestoreStatusResponse, error) {
	var resp RestoreStatusResponse
	if err := c.post(ctx, pathRestoreStart, req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// RestoreStatus returns the current restore status.
func (c *Client) RestoreStatus(ctx context.Context) (*RestoreStatusResponse, error) {
	var resp RestoreStatusResponse
	if err := c.get(ctx, pathRestoreStatus, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// CancelRestore cancels the current restore.
func (c *Client) CancelRestore(ctx context.Context) error {
	var resp struct{}
	return c.post(ctx, pathRestoreCancel, nil, &resp)
}

// ListSnapshots lists all snapshots.
func (c *Client) ListSnapshots(ctx context.Context) ([]SnapshotResponse, error) {
	var resp []SnapshotResponse
	if err := c.get(ctx, pathSnapshots, &resp); err != nil {
		return nil, err
	}
	return resp, nil
}

// GetSnapshot returns a specific snapshot.
func (c *Client) GetSnapshot(ctx context.Context, id string) (*SnapshotResponse, error) {
	var resp SnapshotResponse
	if err := c.get(ctx, fmt.Sprintf("%s/%s", pathSnapshots, id), &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// DeleteSnapshot deletes a snapshot.
func (c *Client) DeleteSnapshot(ctx context.Context, id string) error {
	return c.delete(ctx, fmt.Sprintf("%s/%s", pathSnapshots, id), nil)
}

// ListJobs lists all jobs.
func (c *Client) ListJobs(ctx context.Context) ([]JobResponse, error) {
	var resp []JobResponse
	if err := c.get(ctx, pathJobs, &resp); err != nil {
		return nil, err
	}
	return resp, nil
}

// GetJob returns a specific job.
func (c *Client) GetJob(ctx context.Context, id string) (*JobResponse, error) {
	var resp JobResponse
	if err := c.get(ctx, fmt.Sprintf("%s/%s", pathJobs, id), &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// CancelJob cancels a job.
func (c *Client) CancelJob(ctx context.Context, id string) error {
	var resp struct{}
	return c.post(ctx, fmt.Sprintf("%s/%s/cancel", pathJobs, id), nil, &resp)
}

// ListSchedules lists all schedules.
func (c *Client) ListSchedules(ctx context.Context) ([]ScheduleResponse, error) {
	var resp []ScheduleResponse
	if err := c.get(ctx, pathSchedules, &resp); err != nil {
		return nil, err
	}
	return resp, nil
}

// GetSchedule returns a specific schedule.
func (c *Client) GetSchedule(ctx context.Context, name string) (*ScheduleResponse, error) {
	var resp ScheduleResponse
	if err := c.get(ctx, fmt.Sprintf("%s/%s", pathSchedules, name), &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// CreateSchedule creates a new schedule.
func (c *Client) CreateSchedule(ctx context.Context, req *ScheduleRequest) (*ScheduleResponse, error) {
	var resp ScheduleResponse
	if err := c.post(ctx, pathSchedules, req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// UpdateSchedule updates a schedule.
func (c *Client) UpdateSchedule(ctx context.Context, name string, req *ScheduleRequest) (*ScheduleResponse, error) {
	var resp ScheduleResponse
	if err := c.put(ctx, fmt.Sprintf("%s/%s", pathSchedules, name), req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// DeleteSchedule deletes a schedule.
func (c *Client) DeleteSchedule(ctx context.Context, name string) error {
	return c.delete(ctx, fmt.Sprintf("%s/%s", pathSchedules, name), nil)
}

// TriggerSchedule manually triggers a schedule.
func (c *Client) TriggerSchedule(ctx context.Context, name string) error {
	var resp struct{}
	return c.post(ctx, fmt.Sprintf("%s/%s/trigger", pathSchedules, name), nil, &resp)
}

// GetConfig returns the backend configuration.
func (c *Client) GetConfig(ctx context.Context) (*ConfigResponse, error) {
	var resp ConfigResponse
	if err := c.get(ctx, pathConfig, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// UpdateConfig updates the backend configuration.
func (c *Client) UpdateConfig(ctx context.Context, req *ConfigUpdateRequest) (*ConfigResponse, error) {
	var resp ConfigResponse
	if err := c.put(ctx, pathConfig, req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// GetMode returns the current operating mode.
func (c *Client) GetMode(ctx context.Context) (*ModeResponse, error) {
	var resp ModeResponse
	if err := c.get(ctx, pathMode, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// SwitchMode changes the operating mode.
func (c *Client) SwitchMode(ctx context.Context, req *ModeUpdateRequest) (*ModeResponse, error) {
	var resp ModeResponse
	if err := c.put(ctx, pathMode, req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// ListPaths returns the configured backup paths with details.
func (c *Client) ListPaths(ctx context.Context) (*PathsResponse, error) {
	var resp PathsResponse
	if err := c.get(ctx, pathPaths, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// AddPath adds a new backup path.
func (c *Client) AddPath(ctx context.Context, req *PathAddRequest) (*PathsResponse, error) {
	var resp PathsResponse
	if err := c.post(ctx, pathPaths, req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// RemovePath removes a backup path.
func (c *Client) RemovePath(ctx context.Context, req *PathRemoveRequest) (*PathsResponse, error) {
	var resp PathsResponse
	if err := c.deleteWithBody(ctx, pathPaths, req, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// CredentialsResponse represents S3 credential info (secret not exposed).
type CredentialsResponse struct {
	AccessKeyID    string `json:"access_key_id"`
	BucketName     string `json:"bucket_name"`
	HasCredentials bool   `json:"has_credentials"`
	DataBackend    string `json:"data_backend"`
}

// CredentialRotateResponse represents the result of a credential rotation.
type CredentialRotateResponse struct {
	Status    string `json:"status"`
	AccessKey string `json:"access_key"`
	RotatedAt string `json:"rotated_at"`
	Warning   string `json:"warning,omitempty"`
}

// GetCredentials returns S3 credential info.
func (c *Client) GetCredentials(ctx context.Context) (*CredentialsResponse, error) {
	var resp CredentialsResponse
	if err := c.get(ctx, pathCredentials, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// RotateCredentials rotates the S3 secret access key.
func (c *Client) RotateCredentials(ctx context.Context) (*CredentialRotateResponse, error) {
	var resp CredentialRotateResponse
	if err := c.post(ctx, pathCredRotate, nil, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}
