package tools

import (
	"context"
	"strings"

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

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

type SkillsTool struct {
	store    *store.DB
	sessions *session.ActiveProjects
}

func NewSkillsTool(db *store.DB, sessions *session.ActiveProjects) *SkillsTool {
	return &SkillsTool{store: db, sessions: sessions}
}

// add_skill

type AddSkillInput struct {
	Name        string   `json:"name" jsonschema:"unique skill name"`
	Description string   `json:"description,omitempty" jsonschema:"short description of what the skill does"`
	Content     string   `json:"content" jsonschema:"the full skill instruction or prompt content"`
	Tags        []string `json:"tags,omitempty" jsonschema:"optional tags for grouping or filtering"`
}

type AddSkillOutput struct {
	Skill ext.AgentSkill `json:"skill"`
}

func (t *SkillsTool) AddSkill(ctx context.Context, _ *mcp.CallToolRequest, in AddSkillInput) (*mcp.CallToolResult, AddSkillOutput, error) {
	if strings.TrimSpace(in.Name) == "" {
		return nil, AddSkillOutput{}, errRequiredField("name")
	}
	if strings.TrimSpace(in.Content) == "" {
		return nil, AddSkillOutput{}, errRequiredField("content")
	}
	if in.Tags == nil {
		in.Tags = []string{}
	}
	skill, err := t.store.AddSkill(ctx, ext.AgentSkill{
		Name:        strings.TrimSpace(in.Name),
		Description: strings.TrimSpace(in.Description),
		Content:     strings.TrimSpace(in.Content),
		Tags:        in.Tags,
	})
	if err != nil {
		return nil, AddSkillOutput{}, err
	}
	return nil, AddSkillOutput{Skill: skill}, nil
}

// remove_skill

type RemoveSkillInput struct {
	ID int64 `json:"id" jsonschema:"skill id to remove"`
}

type RemoveSkillOutput struct {
	Removed bool `json:"removed"`
}

func (t *SkillsTool) RemoveSkill(ctx context.Context, _ *mcp.CallToolRequest, in RemoveSkillInput) (*mcp.CallToolResult, RemoveSkillOutput, error) {
	if err := t.store.RemoveSkill(ctx, in.ID); err != nil {
		return nil, RemoveSkillOutput{}, err
	}
	return nil, RemoveSkillOutput{Removed: true}, nil
}

// list_skills

type ListSkillsInput struct {
	Tag string `json:"tag,omitempty" jsonschema:"filter by tag"`
}

type ListSkillsOutput struct {
	Skills []ext.AgentSkill `json:"skills"`
}

func (t *SkillsTool) ListSkills(ctx context.Context, _ *mcp.CallToolRequest, in ListSkillsInput) (*mcp.CallToolResult, ListSkillsOutput, error) {
	skills, err := t.store.ListSkills(ctx, in.Tag)
	if err != nil {
		return nil, ListSkillsOutput{}, err
	}
	if skills == nil {
		skills = []ext.AgentSkill{}
	}
	return nil, ListSkillsOutput{Skills: skills}, nil
}

// add_guardrail

type AddGuardrailInput struct {
	Name        string   `json:"name" jsonschema:"unique guardrail name"`
	Description string   `json:"description,omitempty" jsonschema:"short description of what the guardrail enforces"`
	Content     string   `json:"content" jsonschema:"the full guardrail rule or constraint content"`
	Severity    string   `json:"severity,omitempty" jsonschema:"one of: low, medium, high, critical (default medium)"`
	Tags        []string `json:"tags,omitempty" jsonschema:"optional tags for grouping or filtering"`
}

type AddGuardrailOutput struct {
	Guardrail ext.AgentGuardrail `json:"guardrail"`
}

func (t *SkillsTool) AddGuardrail(ctx context.Context, _ *mcp.CallToolRequest, in AddGuardrailInput) (*mcp.CallToolResult, AddGuardrailOutput, error) {
	if strings.TrimSpace(in.Name) == "" {
		return nil, AddGuardrailOutput{}, errRequiredField("name")
	}
	if strings.TrimSpace(in.Content) == "" {
		return nil, AddGuardrailOutput{}, errRequiredField("content")
	}
	severity := strings.TrimSpace(in.Severity)
	if severity == "" {
		severity = "medium"
	}
	switch severity {
	case "low", "medium", "high", "critical":
	default:
		return nil, AddGuardrailOutput{}, errInvalidField(
			"severity",
			"severity must be one of: low, medium, high, critical",
			"pass one of: low, medium, high, critical",
		)
	}
	if in.Tags == nil {
		in.Tags = []string{}
	}
	guardrail, err := t.store.AddGuardrail(ctx, ext.AgentGuardrail{
		Name:        strings.TrimSpace(in.Name),
		Description: strings.TrimSpace(in.Description),
		Content:     strings.TrimSpace(in.Content),
		Severity:    severity,
		Tags:        in.Tags,
	})
	if err != nil {
		return nil, AddGuardrailOutput{}, err
	}
	return nil, AddGuardrailOutput{Guardrail: guardrail}, nil
}

// remove_guardrail

type RemoveGuardrailInput struct {
	ID int64 `json:"id" jsonschema:"guardrail id to remove"`
}

type RemoveGuardrailOutput struct {
	Removed bool `json:"removed"`
}

func (t *SkillsTool) RemoveGuardrail(ctx context.Context, _ *mcp.CallToolRequest, in RemoveGuardrailInput) (*mcp.CallToolResult, RemoveGuardrailOutput, error) {
	if err := t.store.RemoveGuardrail(ctx, in.ID); err != nil {
		return nil, RemoveGuardrailOutput{}, err
	}
	return nil, RemoveGuardrailOutput{Removed: true}, nil
}

// list_guardrails

type ListGuardrailsInput struct {
	Tag      string `json:"tag,omitempty" jsonschema:"filter by tag"`
	Severity string `json:"severity,omitempty" jsonschema:"filter by severity: low, medium, high, or critical"`
}

type ListGuardrailsOutput struct {
	Guardrails []ext.AgentGuardrail `json:"guardrails"`
}

func (t *SkillsTool) ListGuardrails(ctx context.Context, _ *mcp.CallToolRequest, in ListGuardrailsInput) (*mcp.CallToolResult, ListGuardrailsOutput, error) {
	guardrails, err := t.store.ListGuardrails(ctx, in.Tag, in.Severity)
	if err != nil {
		return nil, ListGuardrailsOutput{}, err
	}
	if guardrails == nil {
		guardrails = []ext.AgentGuardrail{}
	}
	return nil, ListGuardrailsOutput{Guardrails: guardrails}, nil
}

// add_project_skill

type AddProjectSkillInput struct {
	Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
	SkillID int64  `json:"skill_id" jsonschema:"skill id to link"`
}

type AddProjectSkillOutput struct {
	ProjectID int64 `json:"project_id"`
	SkillID   int64 `json:"skill_id"`
}

func (t *SkillsTool) AddProjectSkill(ctx context.Context, req *mcp.CallToolRequest, in AddProjectSkillInput) (*mcp.CallToolResult, AddProjectSkillOutput, error) {
	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, true)
	if err != nil {
		return nil, AddProjectSkillOutput{}, err
	}
	if err := t.store.AddProjectSkill(ctx, project.NumericID, in.SkillID); err != nil {
		return nil, AddProjectSkillOutput{}, err
	}
	return nil, AddProjectSkillOutput{ProjectID: project.NumericID, SkillID: in.SkillID}, nil
}

// remove_project_skill

type RemoveProjectSkillInput struct {
	Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
	SkillID int64  `json:"skill_id" jsonschema:"skill id to unlink"`
}

type RemoveProjectSkillOutput struct {
	Removed bool `json:"removed"`
}

func (t *SkillsTool) RemoveProjectSkill(ctx context.Context, req *mcp.CallToolRequest, in RemoveProjectSkillInput) (*mcp.CallToolResult, RemoveProjectSkillOutput, error) {
	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, true)
	if err != nil {
		return nil, RemoveProjectSkillOutput{}, err
	}
	if err := t.store.RemoveProjectSkill(ctx, project.NumericID, in.SkillID); err != nil {
		return nil, RemoveProjectSkillOutput{}, err
	}
	return nil, RemoveProjectSkillOutput{Removed: true}, nil
}

// list_project_skills

type ListProjectSkillsInput struct {
	Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
}

type ListProjectSkillsOutput struct {
	ProjectID int64            `json:"project_id"`
	Skills    []ext.AgentSkill `json:"skills"`
}

func (t *SkillsTool) ListProjectSkills(ctx context.Context, req *mcp.CallToolRequest, in ListProjectSkillsInput) (*mcp.CallToolResult, ListProjectSkillsOutput, error) {
	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, true)
	if err != nil {
		return nil, ListProjectSkillsOutput{}, err
	}
	skills, err := t.store.ListProjectSkills(ctx, project.NumericID)
	if err != nil {
		return nil, ListProjectSkillsOutput{}, err
	}
	if skills == nil {
		skills = []ext.AgentSkill{}
	}
	return nil, ListProjectSkillsOutput{ProjectID: project.NumericID, Skills: skills}, nil
}

// add_project_guardrail

type AddProjectGuardrailInput struct {
	Project     string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
	GuardrailID int64  `json:"guardrail_id" jsonschema:"guardrail id to link"`
}

type AddProjectGuardrailOutput struct {
	ProjectID   int64 `json:"project_id"`
	GuardrailID int64 `json:"guardrail_id"`
}

func (t *SkillsTool) AddProjectGuardrail(ctx context.Context, req *mcp.CallToolRequest, in AddProjectGuardrailInput) (*mcp.CallToolResult, AddProjectGuardrailOutput, error) {
	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, true)
	if err != nil {
		return nil, AddProjectGuardrailOutput{}, err
	}
	if err := t.store.AddProjectGuardrail(ctx, project.NumericID, in.GuardrailID); err != nil {
		return nil, AddProjectGuardrailOutput{}, err
	}
	return nil, AddProjectGuardrailOutput{ProjectID: project.NumericID, GuardrailID: in.GuardrailID}, nil
}

// remove_project_guardrail

type RemoveProjectGuardrailInput struct {
	Project     string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
	GuardrailID int64  `json:"guardrail_id" jsonschema:"guardrail id to unlink"`
}

type RemoveProjectGuardrailOutput struct {
	Removed bool `json:"removed"`
}

func (t *SkillsTool) RemoveProjectGuardrail(ctx context.Context, req *mcp.CallToolRequest, in RemoveProjectGuardrailInput) (*mcp.CallToolResult, RemoveProjectGuardrailOutput, error) {
	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, true)
	if err != nil {
		return nil, RemoveProjectGuardrailOutput{}, err
	}
	if err := t.store.RemoveProjectGuardrail(ctx, project.NumericID, in.GuardrailID); err != nil {
		return nil, RemoveProjectGuardrailOutput{}, err
	}
	return nil, RemoveProjectGuardrailOutput{Removed: true}, nil
}

// list_project_guardrails

type ListProjectGuardrailsInput struct {
	Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
}

type ListProjectGuardrailsOutput struct {
	ProjectID  int64                `json:"project_id"`
	Guardrails []ext.AgentGuardrail `json:"guardrails"`
}

func (t *SkillsTool) ListProjectGuardrails(ctx context.Context, req *mcp.CallToolRequest, in ListProjectGuardrailsInput) (*mcp.CallToolResult, ListProjectGuardrailsOutput, error) {
	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, true)
	if err != nil {
		return nil, ListProjectGuardrailsOutput{}, err
	}
	guardrails, err := t.store.ListProjectGuardrails(ctx, project.NumericID)
	if err != nil {
		return nil, ListProjectGuardrailsOutput{}, err
	}
	if guardrails == nil {
		guardrails = []ext.AgentGuardrail{}
	}
	return nil, ListProjectGuardrailsOutput{ProjectID: project.NumericID, Guardrails: guardrails}, nil
}
