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 CalendarTool struct {
	store *store.DB
}

func NewCalendarTool(db *store.DB) *CalendarTool {
	return &CalendarTool{store: db}
}

// add_family_member

type AddFamilyMemberInput struct {
	Name         string     `json:"name" jsonschema:"person's name"`
	Relationship string     `json:"relationship,omitempty" jsonschema:"e.g. self, spouse, child, parent"`
	BirthDate    *time.Time `json:"birth_date,omitempty"`
	Notes        string     `json:"notes,omitempty"`
}

type AddFamilyMemberOutput struct {
	Member ext.FamilyMember `json:"member"`
}

func (t *CalendarTool) AddMember(ctx context.Context, _ *mcp.CallToolRequest, in AddFamilyMemberInput) (*mcp.CallToolResult, AddFamilyMemberOutput, error) {
	if strings.TrimSpace(in.Name) == "" {
		return nil, AddFamilyMemberOutput{}, errRequiredField("name")
	}
	member, err := t.store.AddFamilyMember(ctx, ext.FamilyMember{
		Name:         strings.TrimSpace(in.Name),
		Relationship: strings.TrimSpace(in.Relationship),
		BirthDate:    in.BirthDate,
		Notes:        strings.TrimSpace(in.Notes),
	})
	if err != nil {
		return nil, AddFamilyMemberOutput{}, err
	}
	return nil, AddFamilyMemberOutput{Member: member}, nil
}

// list_family_members

type ListFamilyMembersInput struct{}

type ListFamilyMembersOutput struct {
	Members []ext.FamilyMember `json:"members"`
}

func (t *CalendarTool) ListMembers(ctx context.Context, _ *mcp.CallToolRequest, _ ListFamilyMembersInput) (*mcp.CallToolResult, ListFamilyMembersOutput, error) {
	members, err := t.store.ListFamilyMembers(ctx)
	if err != nil {
		return nil, ListFamilyMembersOutput{}, err
	}
	if members == nil {
		members = []ext.FamilyMember{}
	}
	return nil, ListFamilyMembersOutput{Members: members}, nil
}

// add_activity

type AddActivityInput struct {
	Title          string     `json:"title" jsonschema:"activity title"`
	ActivityType   string     `json:"activity_type,omitempty" jsonschema:"e.g. sports, medical, school, social"`
	FamilyMemberID *uuid.UUID `json:"family_member_id,omitempty" jsonschema:"leave empty for whole-family activities"`
	DayOfWeek      string     `json:"day_of_week,omitempty" jsonschema:"for recurring: monday, tuesday, etc."`
	StartTime      string     `json:"start_time,omitempty" jsonschema:"HH:MM format"`
	EndTime        string     `json:"end_time,omitempty" jsonschema:"HH:MM format"`
	StartDate      *time.Time `json:"start_date,omitempty"`
	EndDate        *time.Time `json:"end_date,omitempty" jsonschema:"for recurring activities, when they end"`
	Location       string     `json:"location,omitempty"`
	Notes          string     `json:"notes,omitempty"`
}

type AddActivityOutput struct {
	Activity ext.Activity `json:"activity"`
}

func (t *CalendarTool) AddActivity(ctx context.Context, _ *mcp.CallToolRequest, in AddActivityInput) (*mcp.CallToolResult, AddActivityOutput, error) {
	if strings.TrimSpace(in.Title) == "" {
		return nil, AddActivityOutput{}, errRequiredField("title")
	}
	activity, err := t.store.AddActivity(ctx, ext.Activity{
		FamilyMemberID: in.FamilyMemberID,
		Title:          strings.TrimSpace(in.Title),
		ActivityType:   strings.TrimSpace(in.ActivityType),
		DayOfWeek:      strings.ToLower(strings.TrimSpace(in.DayOfWeek)),
		StartTime:      strings.TrimSpace(in.StartTime),
		EndTime:        strings.TrimSpace(in.EndTime),
		StartDate:      in.StartDate,
		EndDate:        in.EndDate,
		Location:       strings.TrimSpace(in.Location),
		Notes:          strings.TrimSpace(in.Notes),
	})
	if err != nil {
		return nil, AddActivityOutput{}, err
	}
	return nil, AddActivityOutput{Activity: activity}, nil
}

// get_week_schedule

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

type GetWeekScheduleOutput struct {
	Activities []ext.Activity `json:"activities"`
}

func (t *CalendarTool) GetWeekSchedule(ctx context.Context, _ *mcp.CallToolRequest, in GetWeekScheduleInput) (*mcp.CallToolResult, GetWeekScheduleOutput, error) {
	activities, err := t.store.GetWeekSchedule(ctx, in.WeekStart)
	if err != nil {
		return nil, GetWeekScheduleOutput{}, err
	}
	if activities == nil {
		activities = []ext.Activity{}
	}
	return nil, GetWeekScheduleOutput{Activities: activities}, nil
}

// search_activities

type SearchActivitiesInput struct {
	Query          string     `json:"query,omitempty" jsonschema:"search text matching title or notes"`
	ActivityType   string     `json:"activity_type,omitempty" jsonschema:"filter by type"`
	FamilyMemberID *uuid.UUID `json:"family_member_id,omitempty" jsonschema:"filter by family member"`
}

type SearchActivitiesOutput struct {
	Activities []ext.Activity `json:"activities"`
}

func (t *CalendarTool) SearchActivities(ctx context.Context, _ *mcp.CallToolRequest, in SearchActivitiesInput) (*mcp.CallToolResult, SearchActivitiesOutput, error) {
	activities, err := t.store.SearchActivities(ctx, in.Query, in.ActivityType, in.FamilyMemberID)
	if err != nil {
		return nil, SearchActivitiesOutput{}, err
	}
	if activities == nil {
		activities = []ext.Activity{}
	}
	return nil, SearchActivitiesOutput{Activities: activities}, nil
}

// add_important_date

type AddImportantDateInput struct {
	Title              string     `json:"title" jsonschema:"description of the date"`
	DateValue          time.Time  `json:"date_value" jsonschema:"the date"`
	FamilyMemberID     *uuid.UUID `json:"family_member_id,omitempty"`
	RecurringYearly    bool       `json:"recurring_yearly,omitempty" jsonschema:"if true, reminds every year"`
	ReminderDaysBefore int        `json:"reminder_days_before,omitempty" jsonschema:"how many days before to remind (default: 7)"`
	Notes              string     `json:"notes,omitempty"`
}

type AddImportantDateOutput struct {
	Date ext.ImportantDate `json:"date"`
}

func (t *CalendarTool) AddImportantDate(ctx context.Context, _ *mcp.CallToolRequest, in AddImportantDateInput) (*mcp.CallToolResult, AddImportantDateOutput, error) {
	if strings.TrimSpace(in.Title) == "" {
		return nil, AddImportantDateOutput{}, errRequiredField("title")
	}
	reminder := in.ReminderDaysBefore
	if reminder <= 0 {
		reminder = 7
	}
	d, err := t.store.AddImportantDate(ctx, ext.ImportantDate{
		FamilyMemberID:     in.FamilyMemberID,
		Title:              strings.TrimSpace(in.Title),
		DateValue:          in.DateValue,
		RecurringYearly:    in.RecurringYearly,
		ReminderDaysBefore: reminder,
		Notes:              strings.TrimSpace(in.Notes),
	})
	if err != nil {
		return nil, AddImportantDateOutput{}, err
	}
	return nil, AddImportantDateOutput{Date: d}, nil
}

// get_upcoming_dates

type GetUpcomingDatesInput struct {
	DaysAhead int `json:"days_ahead,omitempty" jsonschema:"how many days to look ahead (default: 30)"`
}

type GetUpcomingDatesOutput struct {
	Dates []ext.ImportantDate `json:"dates"`
}

func (t *CalendarTool) GetUpcomingDates(ctx context.Context, _ *mcp.CallToolRequest, in GetUpcomingDatesInput) (*mcp.CallToolResult, GetUpcomingDatesOutput, error) {
	dates, err := t.store.GetUpcomingDates(ctx, in.DaysAhead)
	if err != nil {
		return nil, GetUpcomingDatesOutput{}, err
	}
	if dates == nil {
		dates = []ext.ImportantDate{}
	}
	return nil, GetUpcomingDatesOutput{Dates: dates}, nil
}
