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

package lunatask

import (
	"encoding/json"
	"time"
)

// Source represents an external source integration (e.g., GitHub, Todoist).
// Used across multiple entity types for tracking where items originated.
type Source struct {
	Source   string `json:"source"`
	SourceID string `json:"source_id"`
}

// Date represents a date-only value (YYYY-MM-DD) with proper JSON marshaling.
// Used for fields like scheduled_on that don't include time components.
type Date struct {
	time.Time
}

const dateFormat = "2006-01-02"

// MarshalJSON implements json.Marshaler for Date.
func (d Date) MarshalJSON() ([]byte, error) {
	if d.IsZero() {
		return []byte("null"), nil
	}
	return json.Marshal(d.Format(dateFormat))
}

// UnmarshalJSON implements json.Unmarshaler for Date.
func (d *Date) UnmarshalJSON(data []byte) error {
	if string(data) == "null" {
		return nil
	}

	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}

	t, err := time.Parse(dateFormat, s)
	if err != nil {
		return err
	}

	d.Time = t
	return nil
}

// String returns the date in YYYY-MM-DD format.
func (d Date) String() string {
	if d.IsZero() {
		return ""
	}
	return d.Format(dateFormat)
}

// NewDate creates a Date from a time.Time, discarding time components.
func NewDate(t time.Time) Date {
	return Date{time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.UTC)}
}

// ParseDate parses a date string in YYYY-MM-DD format.
func ParseDate(s string) (Date, error) {
	t, err := time.Parse(dateFormat, s)
	if err != nil {
		return Date{}, err
	}
	return Date{t}, nil
}

// Today returns the current date.
func Today() Date {
	return NewDate(time.Now())
}
