package mcp

import (
	"context"
	"encoding/json"
	"strings"

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

	"github.com/yan1h/agent-coding-workflow/internal/project"
	"github.com/yan1h/agent-coding-workflow/internal/workspace"
)

// registerResources 注册 6 个 PM resource URI template。
//   - pm://projects                          → static resource, lists all projects
//   - pm://projects/{project_id}             → template, single project
//   - pm://projects/{project_id}/requirements → template, list requirements
//   - pm://projects/{project_id}/issues       → template, list issues
//   - pm://projects/{project_id}/workspaces   → template, list workspaces
//   - pm://workspaces/{workspace_id}          → template, single workspace
func registerResources(srv *mcpsdk.Server, deps ServerDeps) {
	// Static resource: pm://projects
	srv.AddResource(&mcpsdk.Resource{
		URI:         "pm://projects",
		Name:        "projects",
		Description: "List all accessible projects",
	}, listProjectsResource(deps))

	// Template resources
	srv.AddResourceTemplate(&mcpsdk.ResourceTemplate{
		URITemplate: "pm://projects/{project_id}",
		Name:        "project-detail",
		Description: "Get a single project by ID",
	}, getProjectResource(deps))

	srv.AddResourceTemplate(&mcpsdk.ResourceTemplate{
		URITemplate: "pm://projects/{project_id}/requirements",
		Name:        "project-requirements",
		Description: "List requirements in a project",
	}, listRequirementsResource(deps))

	srv.AddResourceTemplate(&mcpsdk.ResourceTemplate{
		URITemplate: "pm://projects/{project_id}/issues",
		Name:        "project-issues",
		Description: "List issues in a project",
	}, listIssuesResource(deps))

	srv.AddResourceTemplate(&mcpsdk.ResourceTemplate{
		URITemplate: "pm://projects/{project_id}/workspaces",
		Name:        "project-workspaces",
		Description: "List workspaces in a project",
	}, listWorkspacesResource(deps))

	srv.AddResourceTemplate(&mcpsdk.ResourceTemplate{
		URITemplate: "pm://workspaces/{workspace_id}",
		Name:        "workspace-detail",
		Description: "Get a single workspace by ID",
	}, getWorkspaceResource(deps))
}

func listProjectsResource(deps ServerDeps) mcpsdk.ResourceHandler {
	return func(ctx context.Context, req *mcpsdk.ReadResourceRequest) (*mcpsdk.ReadResourceResult, error) {
		caller, err := deps.Caller.Resolve(ctx)
		if err != nil {
			return nil, err
		}
		projects, err := deps.Projects.List(ctx, caller, false)
		if err != nil {
			return nil, err
		}
		return marshalResourceResult("pm://projects", projects)
	}
}

func getProjectResource(deps ServerDeps) mcpsdk.ResourceHandler {
	return func(ctx context.Context, req *mcpsdk.ReadResourceRequest) (*mcpsdk.ReadResourceResult, error) {
		caller, err := deps.Caller.Resolve(ctx)
		if err != nil {
			return nil, err
		}
		pid, err := extractResourceUUID(req.Params.URI, "projects/")
		if err != nil {
			return nil, err
		}
		if err := CheckProjectFromCtx(ctx, pid); err != nil {
			return nil, err
		}
		p, err := deps.Projects.GetByID(ctx, caller, pid)
		if err != nil {
			return nil, err
		}
		return marshalResourceResult(req.Params.URI, p)
	}
}

func listRequirementsResource(deps ServerDeps) mcpsdk.ResourceHandler {
	return func(ctx context.Context, req *mcpsdk.ReadResourceRequest) (*mcpsdk.ReadResourceResult, error) {
		caller, err := deps.Caller.Resolve(ctx)
		if err != nil {
			return nil, err
		}
		pid, err := extractResourceUUID(req.Params.URI, "projects/")
		if err != nil {
			return nil, err
		}
		if err := CheckProjectFromCtx(ctx, pid); err != nil {
			return nil, err
		}
		p, err := deps.Projects.GetByID(ctx, caller, pid)
		if err != nil {
			return nil, err
		}
		reqs, err := deps.Reqs.List(ctx, caller, p.Slug, project.RequirementFilter{})
		if err != nil {
			return nil, err
		}
		return marshalResourceResult(req.Params.URI, reqs)
	}
}

func listIssuesResource(deps ServerDeps) mcpsdk.ResourceHandler {
	return func(ctx context.Context, req *mcpsdk.ReadResourceRequest) (*mcpsdk.ReadResourceResult, error) {
		caller, err := deps.Caller.Resolve(ctx)
		if err != nil {
			return nil, err
		}
		pid, err := extractResourceUUID(req.Params.URI, "projects/")
		if err != nil {
			return nil, err
		}
		if err := CheckProjectFromCtx(ctx, pid); err != nil {
			return nil, err
		}
		p, err := deps.Projects.GetByID(ctx, caller, pid)
		if err != nil {
			return nil, err
		}
		issues, err := deps.Issues.List(ctx, caller, p.Slug, project.IssueFilter{})
		if err != nil {
			return nil, err
		}
		return marshalResourceResult(req.Params.URI, issues)
	}
}

func listWorkspacesResource(deps ServerDeps) mcpsdk.ResourceHandler {
	return func(ctx context.Context, req *mcpsdk.ReadResourceRequest) (*mcpsdk.ReadResourceResult, error) {
		caller, err := deps.Caller.Resolve(ctx)
		if err != nil {
			return nil, err
		}
		pid, err := extractResourceUUID(req.Params.URI, "projects/")
		if err != nil {
			return nil, err
		}
		if err := CheckProjectFromCtx(ctx, pid); err != nil {
			return nil, err
		}
		p, err := deps.Projects.GetByID(ctx, caller, pid)
		if err != nil {
			return nil, err
		}
		ws, err := deps.Workspaces.List(ctx, workspaceCallerFrom(caller), p.Slug)
		if err != nil {
			return nil, err
		}
		return marshalResourceResult(req.Params.URI, ws)
	}
}

func getWorkspaceResource(deps ServerDeps) mcpsdk.ResourceHandler {
	return func(ctx context.Context, req *mcpsdk.ReadResourceRequest) (*mcpsdk.ReadResourceResult, error) {
		caller, err := deps.Caller.Resolve(ctx)
		if err != nil {
			return nil, err
		}
		wid, err := extractResourceUUID(req.Params.URI, "workspaces/")
		if err != nil {
			return nil, err
		}
		w, err := deps.Workspaces.Get(ctx, workspaceCallerFrom(caller), wid)
		if err != nil {
			return nil, err
		}
		return marshalResourceResult(req.Params.URI, w)
	}
}

// workspaceCallerFrom bridges project.Caller → workspace.Caller.
func workspaceCallerFrom(c project.Caller) workspace.Caller {
	return workspace.Caller{UserID: c.UserID, IsAdmin: c.IsAdmin}
}

// extractResourceUUID extracts a UUID from a pm:// URI like "pm://projects/{uuid}/requirements".
func extractResourceUUID(uri, prefix string) (uuid.UUID, error) {
	s := uri
	if strings.HasPrefix(s, "pm://") {
		s = s[len("pm://"):]
	}
	if strings.HasPrefix(s, prefix) {
		s = s[len(prefix):]
	}
	// Trim trailing path segments like "/requirements"
	if idx := strings.Index(s, "/"); idx >= 0 {
		s = s[:idx]
	}
	return uuid.Parse(s)
}

// marshalResourceResult marshals v as JSON text content for resource responses.
func marshalResourceResult(uri string, v any) (*mcpsdk.ReadResourceResult, error) {
	data, err := json.Marshal(v)
	if err != nil {
		return nil, err
	}
	return &mcpsdk.ReadResourceResult{
		Contents: []*mcpsdk.ResourceContents{{
			URI:      uri,
			MIMEType: "application/json",
			Text:     string(data),
		}},
	}, nil
}
