package tools

import (
	"context"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/modelcontextprotocol/go-sdk/mcp"

	"git.warky.dev/wdevs/amcs/internal/store"
	ext "git.warky.dev/wdevs/amcs/internal/types"
)

type MealsTool struct {
	store *store.DB
}

func NewMealsTool(db *store.DB) *MealsTool {
	return &MealsTool{store: db}
}

// add_recipe

type AddRecipeInput struct {
	Name            string           `json:"name" jsonschema:"recipe name"`
	Cuisine         string           `json:"cuisine,omitempty"`
	PrepTimeMinutes *int             `json:"prep_time_minutes,omitempty"`
	CookTimeMinutes *int             `json:"cook_time_minutes,omitempty"`
	Servings        *int             `json:"servings,omitempty"`
	Ingredients     []ext.Ingredient `json:"ingredients,omitempty" jsonschema:"list of ingredients with name, quantity, unit"`
	Instructions    []string         `json:"instructions,omitempty" jsonschema:"ordered list of steps"`
	Tags            []string         `json:"tags,omitempty"`
	Rating          *int             `json:"rating,omitempty" jsonschema:"1-5 rating"`
	Notes           string           `json:"notes,omitempty"`
}

type AddRecipeOutput struct {
	Recipe ext.Recipe `json:"recipe"`
}

func (t *MealsTool) AddRecipe(ctx context.Context, _ *mcp.CallToolRequest, in AddRecipeInput) (*mcp.CallToolResult, AddRecipeOutput, error) {
	if strings.TrimSpace(in.Name) == "" {
		return nil, AddRecipeOutput{}, errRequiredField("name")
	}
	if in.Ingredients == nil {
		in.Ingredients = []ext.Ingredient{}
	}
	if in.Instructions == nil {
		in.Instructions = []string{}
	}
	if in.Tags == nil {
		in.Tags = []string{}
	}
	recipe, err := t.store.AddRecipe(ctx, ext.Recipe{
		Name:            strings.TrimSpace(in.Name),
		Cuisine:         strings.TrimSpace(in.Cuisine),
		PrepTimeMinutes: in.PrepTimeMinutes,
		CookTimeMinutes: in.CookTimeMinutes,
		Servings:        in.Servings,
		Ingredients:     in.Ingredients,
		Instructions:    in.Instructions,
		Tags:            in.Tags,
		Rating:          in.Rating,
		Notes:           strings.TrimSpace(in.Notes),
	})
	if err != nil {
		return nil, AddRecipeOutput{}, err
	}
	return nil, AddRecipeOutput{Recipe: recipe}, nil
}

// search_recipes

type SearchRecipesInput struct {
	Query      string   `json:"query,omitempty" jsonschema:"search by recipe name"`
	Cuisine    string   `json:"cuisine,omitempty"`
	Tags       []string `json:"tags,omitempty" jsonschema:"filter by tags (all must match)"`
	Ingredient string   `json:"ingredient,omitempty" jsonschema:"filter by ingredient name"`
}

type SearchRecipesOutput struct {
	Recipes []ext.Recipe `json:"recipes"`
}

func (t *MealsTool) SearchRecipes(ctx context.Context, _ *mcp.CallToolRequest, in SearchRecipesInput) (*mcp.CallToolResult, SearchRecipesOutput, error) {
	recipes, err := t.store.SearchRecipes(ctx, in.Query, in.Cuisine, in.Tags, in.Ingredient)
	if err != nil {
		return nil, SearchRecipesOutput{}, err
	}
	if recipes == nil {
		recipes = []ext.Recipe{}
	}
	return nil, SearchRecipesOutput{Recipes: recipes}, nil
}

// update_recipe

type UpdateRecipeInput struct {
	ID              uuid.UUID        `json:"id" jsonschema:"recipe id to update"`
	Name            string           `json:"name" jsonschema:"recipe name"`
	Cuisine         string           `json:"cuisine,omitempty"`
	PrepTimeMinutes *int             `json:"prep_time_minutes,omitempty"`
	CookTimeMinutes *int             `json:"cook_time_minutes,omitempty"`
	Servings        *int             `json:"servings,omitempty"`
	Ingredients     []ext.Ingredient `json:"ingredients,omitempty"`
	Instructions    []string         `json:"instructions,omitempty"`
	Tags            []string         `json:"tags,omitempty"`
	Rating          *int             `json:"rating,omitempty"`
	Notes           string           `json:"notes,omitempty"`
}

type UpdateRecipeOutput struct {
	Recipe ext.Recipe `json:"recipe"`
}

func (t *MealsTool) UpdateRecipe(ctx context.Context, _ *mcp.CallToolRequest, in UpdateRecipeInput) (*mcp.CallToolResult, UpdateRecipeOutput, error) {
	if strings.TrimSpace(in.Name) == "" {
		return nil, UpdateRecipeOutput{}, errRequiredField("name")
	}
	if in.Ingredients == nil {
		in.Ingredients = []ext.Ingredient{}
	}
	if in.Instructions == nil {
		in.Instructions = []string{}
	}
	if in.Tags == nil {
		in.Tags = []string{}
	}
	recipe, err := t.store.UpdateRecipe(ctx, in.ID, ext.Recipe{
		Name:            strings.TrimSpace(in.Name),
		Cuisine:         strings.TrimSpace(in.Cuisine),
		PrepTimeMinutes: in.PrepTimeMinutes,
		CookTimeMinutes: in.CookTimeMinutes,
		Servings:        in.Servings,
		Ingredients:     in.Ingredients,
		Instructions:    in.Instructions,
		Tags:            in.Tags,
		Rating:          in.Rating,
		Notes:           strings.TrimSpace(in.Notes),
	})
	if err != nil {
		return nil, UpdateRecipeOutput{}, err
	}
	return nil, UpdateRecipeOutput{Recipe: recipe}, nil
}

// create_meal_plan

type CreateMealPlanInput struct {
	WeekStart time.Time           `json:"week_start" jsonschema:"Monday of the week to plan"`
	Meals     []ext.MealPlanInput `json:"meals" jsonschema:"list of meal entries for the week"`
}

type CreateMealPlanOutput struct {
	Entries []ext.MealPlanEntry `json:"entries"`
}

func (t *MealsTool) CreateMealPlan(ctx context.Context, _ *mcp.CallToolRequest, in CreateMealPlanInput) (*mcp.CallToolResult, CreateMealPlanOutput, error) {
	if len(in.Meals) == 0 {
		return nil, CreateMealPlanOutput{}, errInvalidInput("meals are required")
	}
	entries, err := t.store.CreateMealPlan(ctx, in.WeekStart, in.Meals)
	if err != nil {
		return nil, CreateMealPlanOutput{}, err
	}
	if entries == nil {
		entries = []ext.MealPlanEntry{}
	}
	return nil, CreateMealPlanOutput{Entries: entries}, nil
}

// get_meal_plan

type GetMealPlanInput struct {
	WeekStart time.Time `json:"week_start" jsonschema:"Monday of the week to retrieve"`
}

type GetMealPlanOutput struct {
	Entries []ext.MealPlanEntry `json:"entries"`
}

func (t *MealsTool) GetMealPlan(ctx context.Context, _ *mcp.CallToolRequest, in GetMealPlanInput) (*mcp.CallToolResult, GetMealPlanOutput, error) {
	entries, err := t.store.GetMealPlan(ctx, in.WeekStart)
	if err != nil {
		return nil, GetMealPlanOutput{}, err
	}
	if entries == nil {
		entries = []ext.MealPlanEntry{}
	}
	return nil, GetMealPlanOutput{Entries: entries}, nil
}

// generate_shopping_list

type GenerateShoppingListInput struct {
	WeekStart time.Time `json:"week_start" jsonschema:"Monday of the week to generate shopping list for"`
}

type GenerateShoppingListOutput struct {
	ShoppingList ext.ShoppingList `json:"shopping_list"`
}

func (t *MealsTool) GenerateShoppingList(ctx context.Context, _ *mcp.CallToolRequest, in GenerateShoppingListInput) (*mcp.CallToolResult, GenerateShoppingListOutput, error) {
	list, err := t.store.GenerateShoppingList(ctx, in.WeekStart)
	if err != nil {
		return nil, GenerateShoppingListOutput{}, err
	}
	return nil, GenerateShoppingListOutput{ShoppingList: list}, nil
}
