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

package lunatask

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"

	"github.com/go-playground/validator/v10"
)

// TrackHabitActivityRequest represents the request to track a habit activity
type TrackHabitActivityRequest struct {
	PerformedOn string `json:"performed_on" validate:"required,datetime=2006-01-02T15:04:05Z07:00"`
}

// TrackHabitActivityResponse represents the response from Lunatask API when tracking a habit activity
type TrackHabitActivityResponse struct {
	Status  string `json:"status"`
	Message string `json:"message,omitempty"`
}

// ValidateTrackHabitActivity validates the track habit activity request
func ValidateTrackHabitActivity(request *TrackHabitActivityRequest) error {
	validate := validator.New()
	if err := validate.Struct(request); err != nil {
		var invalidValidationError *validator.InvalidValidationError
		if errors.As(err, &invalidValidationError) {
			return fmt.Errorf("invalid validation error: %w", err)
		}

		var validationErrs validator.ValidationErrors
		if errors.As(err, &validationErrs) {
			var msgBuilder strings.Builder
			msgBuilder.WriteString("habit activity validation failed:")
			for _, e := range validationErrs {
				fmt.Fprintf(&msgBuilder, " field '%s' failed '%s' validation (was: %v);", e.Field(), e.Tag(), e.Value())
			}
			return errors.New(msgBuilder.String())
		}
		return fmt.Errorf("validation error: %w", err)
	}
	return nil
}

// TrackHabitActivity tracks an activity for a habit in Lunatask
func (c *Client) TrackHabitActivity(ctx context.Context, habitID string, request *TrackHabitActivityRequest) (*TrackHabitActivityResponse, error) {
	if habitID == "" {
		return nil, errors.New("habit ID cannot be empty")
	}

	// Validate the request
	if err := ValidateTrackHabitActivity(request); err != nil {
		return nil, err
	}

	// Marshal the request to JSON
	payloadBytes, err := json.Marshal(request)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal payload: %w", err)
	}

	// Create the request
	req, err := http.NewRequestWithContext(
		ctx,
		"POST",
		fmt.Sprintf("%s/habits/%s/track", c.BaseURL, habitID),
		bytes.NewBuffer(payloadBytes),
	)
	if err != nil {
		return nil, fmt.Errorf("failed to create HTTP request: %w", err)
	}

	// Set headers
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "bearer "+c.AccessToken)

	// Send the request
	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send HTTP request: %w", err)
	}
	defer func() {
		if resp.Body != nil {
			if err := resp.Body.Close(); err != nil {
				// We're in a defer, so we can only log the error
				fmt.Printf("Error closing response body: %v\n", err)
			}
		}
	}()

	// Handle error status codes
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		respBody, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody))
	}

	// Read and parse the response
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}

	var response TrackHabitActivityResponse
	err = json.Unmarshal(respBody, &response)
	if err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	return &response, nil
}
