// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package lunatask

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// Task represents a task returned from the Lunatask API
type Task struct {
	ID             string     `json:"id"`
	AreaID         *string    `json:"area_id"`
	GoalID         *string    `json:"goal_id"`
	Name           *string    `json:"name"`
	Note           *string    `json:"note"`
	Status         *string    `json:"status"`
	PreviousStatus *string    `json:"previous_status"`
	Estimate       *int       `json:"estimate"`
	Priority       *int       `json:"priority"`
	Progress       *int       `json:"progress"`
	Motivation     *string    `json:"motivation"`
	Eisenhower     *int       `json:"eisenhower"`
	Sources        []Source   `json:"sources"`
	ScheduledOn    *Date      `json:"scheduled_on"`
	CompletedAt    *time.Time `json:"completed_at"`
	CreatedAt      time.Time  `json:"created_at"`
	UpdatedAt      time.Time  `json:"updated_at"`
}

// CreateTaskRequest represents the request to create a task in Lunatask
type CreateTaskRequest struct {
	Name        string     `json:"name"`
	AreaID      *string    `json:"area_id,omitempty"`
	GoalID      *string    `json:"goal_id,omitempty"`
	Note        *string    `json:"note,omitempty"`
	Status      *string    `json:"status,omitempty"`
	Motivation  *string    `json:"motivation,omitempty"`
	Estimate    *int       `json:"estimate,omitempty"`
	Priority    *int       `json:"priority,omitempty"`
	Eisenhower  *int       `json:"eisenhower,omitempty"`
	ScheduledOn *Date      `json:"scheduled_on,omitempty"`
	CompletedAt *time.Time `json:"completed_at,omitempty"`
	Source      *string    `json:"source,omitempty"`
	SourceID    *string    `json:"source_id,omitempty"`
}

// UpdateTaskRequest represents the request to update a task in Lunatask.
// All fields are optional; only provided fields will be updated.
type UpdateTaskRequest struct {
	Name        *string    `json:"name,omitempty"`
	AreaID      *string    `json:"area_id,omitempty"`
	GoalID      *string    `json:"goal_id,omitempty"`
	Note        *string    `json:"note,omitempty"`
	Status      *string    `json:"status,omitempty"`
	Motivation  *string    `json:"motivation,omitempty"`
	Estimate    *int       `json:"estimate,omitempty"`
	Priority    *int       `json:"priority,omitempty"`
	Eisenhower  *int       `json:"eisenhower,omitempty"`
	ScheduledOn *Date      `json:"scheduled_on,omitempty"`
	CompletedAt *time.Time `json:"completed_at,omitempty"`
}

// taskResponse represents a single task response from the API
type taskResponse struct {
	Task Task `json:"task"`
}

// tasksResponse represents a list of tasks response from the API
type tasksResponse struct {
	Tasks []Task `json:"tasks"`
}

// ListTasksOptions contains optional filters for listing tasks
type ListTasksOptions struct {
	Source   *string
	SourceID *string
}

// ListTasks retrieves all tasks, optionally filtered by source and/or source_id
func (c *Client) ListTasks(ctx context.Context, opts *ListTasksOptions) ([]Task, error) {
	path := "/tasks"

	if opts != nil {
		params := url.Values{}
		if opts.Source != nil && *opts.Source != "" {
			params.Set("source", *opts.Source)
		}
		if opts.SourceID != nil && *opts.SourceID != "" {
			params.Set("source_id", *opts.SourceID)
		}
		if len(params) > 0 {
			path = fmt.Sprintf("%s?%s", path, params.Encode())
		}
	}

	resp, _, err := doJSON[tasksResponse](c, ctx, http.MethodGet, path, nil)
	if err != nil {
		return nil, err
	}

	return resp.Tasks, nil
}

// GetTask retrieves a specific task by ID
func (c *Client) GetTask(ctx context.Context, taskID string) (*Task, error) {
	if taskID == "" {
		return nil, fmt.Errorf("%w: task ID cannot be empty", ErrBadRequest)
	}

	resp, _, err := doJSON[taskResponse](c, ctx, http.MethodGet, "/tasks/"+taskID, nil)
	if err != nil {
		return nil, err
	}

	return &resp.Task, nil
}

// CreateTask creates a new task in Lunatask.
// Returns nil, nil if a matching task already exists (HTTP 204).
func (c *Client) CreateTask(ctx context.Context, task *CreateTaskRequest) (*Task, error) {
	if task.Name == "" {
		return nil, fmt.Errorf("%w: name is required", ErrBadRequest)
	}

	resp, noContent, err := doJSON[taskResponse](c, ctx, http.MethodPost, "/tasks", task)
	if err != nil {
		return nil, err
	}
	if noContent {
		return nil, nil
	}

	return &resp.Task, nil
}

// UpdateTask updates an existing task in Lunatask
func (c *Client) UpdateTask(ctx context.Context, taskID string, task *UpdateTaskRequest) (*Task, error) {
	if taskID == "" {
		return nil, fmt.Errorf("%w: task ID cannot be empty", ErrBadRequest)
	}

	resp, _, err := doJSON[taskResponse](c, ctx, http.MethodPut, "/tasks/"+taskID, task)
	if err != nil {
		return nil, err
	}

	return &resp.Task, nil
}

// DeleteTask deletes a task in Lunatask
func (c *Client) DeleteTask(ctx context.Context, taskID string) (*Task, error) {
	if taskID == "" {
		return nil, fmt.Errorf("%w: task ID cannot be empty", ErrBadRequest)
	}

	resp, _, err := doJSON[taskResponse](c, ctx, http.MethodDelete, "/tasks/"+taskID, nil)
	if err != nil {
		return nil, err
	}

	return &resp.Task, nil
}
