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

package lunatask

import "time"

// TaskBuilder constructs a [CreateTaskRequest] via method chaining.
//
//	req := lunatask.NewTask("Review PR").
//		InArea(areaID).
//		WithStatus("next").
//		WithEstimate(30).
//		Build()
//	task, err := client.CreateTask(ctx, req)
type TaskBuilder struct {
	req CreateTaskRequest
}

// NewTask starts building a task with the given name.
func NewTask(name string) *TaskBuilder {
	return &TaskBuilder{req: CreateTaskRequest{Name: name}} //nolint:exhaustruct
}

// InArea assigns the task to an area. IDs are in the area's settings in the app.
func (b *TaskBuilder) InArea(areaID string) *TaskBuilder {
	b.req.AreaID = &areaID

	return b
}

// InGoal assigns the task to a goal. IDs are in the goal's settings in the app.
func (b *TaskBuilder) InGoal(goalID string) *TaskBuilder {
	b.req.GoalID = &goalID

	return b
}

// WithNote attaches a Markdown note to the task.
func (b *TaskBuilder) WithNote(note string) *TaskBuilder {
	b.req.Note = &note

	return b
}

// WithStatus sets the workflow status: later, next, started, waiting, or completed.
func (b *TaskBuilder) WithStatus(status string) *TaskBuilder {
	b.req.Status = &status

	return b
}

// WithMotivation sets why this task matters: must, should, or want.
func (b *TaskBuilder) WithMotivation(motivation string) *TaskBuilder {
	b.req.Motivation = &motivation

	return b
}

// WithEstimate sets the expected duration in minutes (0–720).
func (b *TaskBuilder) WithEstimate(minutes int) *TaskBuilder {
	b.req.Estimate = &minutes

	return b
}

// WithPriority sets importance from -2 (lowest) to 2 (highest).
func (b *TaskBuilder) WithPriority(priority int) *TaskBuilder {
	b.req.Priority = &priority

	return b
}

// WithEisenhower sets the matrix quadrant (0–4).
func (b *TaskBuilder) WithEisenhower(eisenhower int) *TaskBuilder {
	b.req.Eisenhower = &eisenhower

	return b
}

// ScheduledOn sets when the task should appear on your schedule.
func (b *TaskBuilder) ScheduledOn(date Date) *TaskBuilder {
	b.req.ScheduledOn = &date

	return b
}

// CompletedAt marks the task completed at a specific time.
func (b *TaskBuilder) CompletedAt(t time.Time) *TaskBuilder {
	b.req.CompletedAt = &t

	return b
}

// FromSource tags the task with a free-form origin identifier, useful for
// tracking tasks created by scripts or external integrations.
func (b *TaskBuilder) FromSource(source, sourceID string) *TaskBuilder {
	b.req.Source = &source
	b.req.SourceID = &sourceID

	return b
}

// Build returns the constructed request.
func (b *TaskBuilder) Build() *CreateTaskRequest {
	return &b.req
}

// TaskUpdateBuilder constructs an [UpdateTaskRequest] via method chaining.
// Only fields you set will be modified; others remain unchanged.
//
//	req := lunatask.NewTaskUpdate().
//		WithStatus("completed").
//		CompletedAt(time.Now()).
//		Build()
//	task, err := client.UpdateTask(ctx, taskID, req)
type TaskUpdateBuilder struct {
	req UpdateTaskRequest
}

// NewTaskUpdate starts building a task update.
func NewTaskUpdate() *TaskUpdateBuilder {
	return &TaskUpdateBuilder{} //nolint:exhaustruct
}

// Name changes the task's name.
func (b *TaskUpdateBuilder) Name(name string) *TaskUpdateBuilder {
	b.req.Name = &name

	return b
}

// InArea moves the task to an area. IDs are in the area's settings in the app.
func (b *TaskUpdateBuilder) InArea(areaID string) *TaskUpdateBuilder {
	b.req.AreaID = &areaID

	return b
}

// InGoal moves the task to a goal. IDs are in the goal's settings in the app.
func (b *TaskUpdateBuilder) InGoal(goalID string) *TaskUpdateBuilder {
	b.req.GoalID = &goalID

	return b
}

// WithNote replaces the task's Markdown note.
func (b *TaskUpdateBuilder) WithNote(note string) *TaskUpdateBuilder {
	b.req.Note = &note

	return b
}

// WithStatus sets the workflow status: later, next, started, waiting, or completed.
func (b *TaskUpdateBuilder) WithStatus(status string) *TaskUpdateBuilder {
	b.req.Status = &status

	return b
}

// WithMotivation sets why this task matters: must, should, or want.
func (b *TaskUpdateBuilder) WithMotivation(motivation string) *TaskUpdateBuilder {
	b.req.Motivation = &motivation

	return b
}

// WithEstimate sets the expected duration in minutes (0–720).
func (b *TaskUpdateBuilder) WithEstimate(minutes int) *TaskUpdateBuilder {
	b.req.Estimate = &minutes

	return b
}

// WithPriority sets importance from -2 (lowest) to 2 (highest).
func (b *TaskUpdateBuilder) WithPriority(priority int) *TaskUpdateBuilder {
	b.req.Priority = &priority

	return b
}

// WithEisenhower sets the matrix quadrant (0–4).
func (b *TaskUpdateBuilder) WithEisenhower(eisenhower int) *TaskUpdateBuilder {
	b.req.Eisenhower = &eisenhower

	return b
}

// ScheduledOn sets when the task should appear on your schedule.
func (b *TaskUpdateBuilder) ScheduledOn(date Date) *TaskUpdateBuilder {
	b.req.ScheduledOn = &date

	return b
}

// CompletedAt marks the task completed at a specific time.
func (b *TaskUpdateBuilder) CompletedAt(t time.Time) *TaskUpdateBuilder {
	b.req.CompletedAt = &t

	return b
}

// Build returns the constructed request.
func (b *TaskUpdateBuilder) Build() *UpdateTaskRequest {
	return &b.req
}

// NoteBuilder constructs a [CreateNoteRequest] via method chaining.
// Note fields are encrypted client-side by Lunatask; the API accepts them
// on create but returns null on read.
//
//	req := lunatask.NewNote().
//		WithName("Meeting notes").
//		WithContent("# Summary\n\n...").
//		InNotebook(notebookID).
//		Build()
//	note, err := client.CreateNote(ctx, req)
type NoteBuilder struct {
	req CreateNoteRequest
}

// NewNote starts building a note.
func NewNote() *NoteBuilder {
	return &NoteBuilder{} //nolint:exhaustruct
}

// WithName sets the note's title.
func (b *NoteBuilder) WithName(name string) *NoteBuilder {
	b.req.Name = &name

	return b
}

// WithContent sets the Markdown body.
func (b *NoteBuilder) WithContent(content string) *NoteBuilder {
	b.req.Content = &content

	return b
}

// InNotebook places the note in a notebook. IDs are in the notebook's settings in the app.
func (b *NoteBuilder) InNotebook(notebookID string) *NoteBuilder {
	b.req.NotebookID = &notebookID

	return b
}

// FromSource tags the note with a free-form origin identifier, useful for
// tracking notes created by scripts or external integrations.
func (b *NoteBuilder) FromSource(source, sourceID string) *NoteBuilder {
	b.req.Source = &source
	b.req.SourceID = &sourceID

	return b
}

// Build returns the constructed request.
func (b *NoteBuilder) Build() *CreateNoteRequest {
	return &b.req
}

// JournalEntryBuilder constructs a [CreateJournalEntryRequest] via method chaining.
// Journal content is encrypted client-side; the API accepts it on create but
// returns null on read.
//
//	req := lunatask.NewJournalEntry(lunatask.Today()).
//		WithContent("Shipped the new feature!").
//		Build()
//	entry, err := client.CreateJournalEntry(ctx, req)
type JournalEntryBuilder struct {
	req CreateJournalEntryRequest
}

// NewJournalEntry starts building a journal entry for the given date.
func NewJournalEntry(date Date) *JournalEntryBuilder {
	return &JournalEntryBuilder{req: CreateJournalEntryRequest{DateOn: date}} //nolint:exhaustruct
}

// WithName sets the entry's title. Defaults to the weekday name if omitted.
func (b *JournalEntryBuilder) WithName(name string) *JournalEntryBuilder {
	b.req.Name = &name

	return b
}

// WithContent sets the Markdown body.
func (b *JournalEntryBuilder) WithContent(content string) *JournalEntryBuilder {
	b.req.Content = &content

	return b
}

// Build returns the constructed request.
func (b *JournalEntryBuilder) Build() *CreateJournalEntryRequest {
	return &b.req
}

// PersonBuilder constructs a [CreatePersonRequest] via method chaining.
// Name fields are encrypted client-side; the API accepts them on create but
// returns null on read.
//
//	req := lunatask.NewPerson().
//		WithFirstName("Ada").
//		WithLastName("Lovelace").
//		WithRelationshipStrength("close").
//		Build()
//	person, err := client.CreatePerson(ctx, req)
type PersonBuilder struct {
	req CreatePersonRequest
}

// NewPerson starts building a person entry.
func NewPerson() *PersonBuilder {
	return &PersonBuilder{} //nolint:exhaustruct
}

// WithFirstName sets the person's first name.
func (b *PersonBuilder) WithFirstName(name string) *PersonBuilder {
	b.req.FirstName = &name

	return b
}

// WithLastName sets the person's last name.
func (b *PersonBuilder) WithLastName(name string) *PersonBuilder {
	b.req.LastName = &name

	return b
}

// WithRelationshipStrength categorizes the closeness of the relationship.
// Use one of the Relationship* constants (e.g., RelationshipCloseFriend).
func (b *PersonBuilder) WithRelationshipStrength(strength RelationshipStrength) *PersonBuilder {
	b.req.RelationshipStrength = &strength

	return b
}

// FromSource tags the person with a free-form origin identifier, useful for
// tracking entries created by scripts or external integrations.
func (b *PersonBuilder) FromSource(source, sourceID string) *PersonBuilder {
	b.req.Source = &source
	b.req.SourceID = &sourceID

	return b
}

// WithCustomField sets an arbitrary custom field. Lunatask supports "email",
// "birthday", and "phone" out of the box; other fields must first be defined
// in the app or [ErrUnprocessableEntity] is returned.
func (b *PersonBuilder) WithCustomField(key string, value any) *PersonBuilder {
	if b.req.CustomFields == nil {
		b.req.CustomFields = make(map[string]any)
	}

	b.req.CustomFields[key] = value

	return b
}

// Build returns the constructed request.
func (b *PersonBuilder) Build() *CreatePersonRequest {
	return &b.req
}

// TimelineNoteBuilder constructs a [CreatePersonTimelineNoteRequest] via method chaining.
// Content is encrypted client-side; the API accepts it on create but returns null on read.
//
//	req := lunatask.NewTimelineNote(personID).
//		OnDate(lunatask.Today()).
//		WithContent("Had coffee, discussed the project.").
//		Build()
//	note, err := client.CreatePersonTimelineNote(ctx, req)
type TimelineNoteBuilder struct {
	req CreatePersonTimelineNoteRequest
}

// NewTimelineNote starts building a timeline note for the given person.
// Get person IDs from [Client.ListPeople].
func NewTimelineNote(personID string) *TimelineNoteBuilder {
	return &TimelineNoteBuilder{req: CreatePersonTimelineNoteRequest{PersonID: personID}} //nolint:exhaustruct
}

// OnDate sets when this interaction occurred.
func (b *TimelineNoteBuilder) OnDate(date Date) *TimelineNoteBuilder {
	b.req.DateOn = &date

	return b
}

// WithContent sets the Markdown body describing the interaction.
func (b *TimelineNoteBuilder) WithContent(content string) *TimelineNoteBuilder {
	b.req.Content = &content

	return b
}

// Build returns the constructed request.
func (b *TimelineNoteBuilder) Build() *CreatePersonTimelineNoteRequest {
	return &b.req
}
