package agent

import (
	"context"
	"errors"
	"io"
	"mime"
	"strconv"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/service/approvalapp"
)

type AgentSkillPrincipal struct {
	WorkspaceID string
	Subject     string
}

type AgentSkillListInput struct {
	Query          string
	Page           int
	PerPage        int
	TemplateLocale string
	View           string
}

type AgentSkillUpdateInput struct {
	Name        *string
	Description *string
}

// AgentSkillGateway is the thin transport port to the existing Resource Skill
// API. It carries the server-resolved Human subject and no new lifecycle state.
type AgentSkillGateway interface {
	List(context.Context, AgentSkillPrincipal, AgentSkillListInput) (dto.SkillListResponse, error)
	Get(context.Context, AgentSkillPrincipal, string) (dto.Skill, error)
	Create(context.Context, AgentSkillPrincipal, dto.SkillCreateRequest) (dto.Skill, error)
	Update(context.Context, AgentSkillPrincipal, string, AgentSkillUpdateInput) (dto.Skill, error)
	Import(context.Context, AgentSkillPrincipal, string, string, io.Reader) (dto.SkillImportResponse, error)
	Delete(context.Context, AgentSkillPrincipal, string) error
}

type AgentSkillHandler struct {
	owners     AgentSkillOwnerResolver
	workspaces AgentSkillWorkspaceResolver
	skills     AgentSkillGateway
	approvals  ApprovalRuntime
}

type agentSkillGetRequest struct {
	SkillID string `json:"skillId"`
}

type agentSkillUpdateRequest struct {
	SkillID     string  `json:"skillId"`
	Name        *string `json:"name,omitempty"`
	Description *string `json:"description,omitempty"`
}

type agentSkillDeleteRequest struct {
	RequestID string `json:"requestId"`
	SkillID   string `json:"skillId"`
}

type agentSkillDeleteResponse struct {
	ApprovalID string `json:"approvalId"`
	Status     string `json:"status"`
}

func (handler *AgentSkillHandler) List(request api.AgentScopedRequest) (api.Response, error) {
	principal, err := handler.resolvePrincipal(request.Context(), request.Principal)
	if err != nil {
		return api.Response{}, err
	}
	page, err := parseAgentSkillPositiveInt(request.Gin.Query("page"), "page", 0)
	if err != nil {
		return api.Response{}, err
	}
	perPage, err := parseAgentSkillPositiveInt(request.Gin.Query("per_page"), "per_page", 100)
	if err != nil {
		return api.Response{}, err
	}
	result, err := handler.skills.List(request.Context(), principal, AgentSkillListInput{
		Query: request.Gin.Query("q"), Page: page, PerPage: perPage,
		TemplateLocale: request.Gin.Query("template_locale"), View: request.Gin.Query("view"),
	})
	if err != nil {
		return api.Response{}, err
	}
	if result.Items == nil {
		result.Items = []dto.Skill{}
	}
	return api.OK(result), nil
}

func (handler *AgentSkillHandler) Get(request api.AgentScopedJSON[agentSkillGetRequest]) (api.Response, error) {
	skillID := strings.TrimSpace(request.Body.SkillID)
	if skillID == "" {
		return api.Response{}, badRequest("skillId is required")
	}
	principal, err := handler.resolvePrincipal(request.Context(), request.Principal)
	if err != nil {
		return api.Response{}, err
	}
	result, err := handler.skills.Get(request.Context(), principal, skillID)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(result), nil
}

func (handler *AgentSkillHandler) Create(request api.AgentScopedJSON[dto.SkillCreateRequest]) (api.Response, error) {
	principal, err := handler.resolvePrincipal(request.Context(), request.Principal)
	if err != nil {
		return api.Response{}, err
	}
	result, err := handler.skills.Create(request.Context(), principal, request.Body)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(result), nil
}

func (handler *AgentSkillHandler) Update(request api.AgentScopedJSON[agentSkillUpdateRequest]) (api.Response, error) {
	skillID := strings.TrimSpace(request.Body.SkillID)
	if skillID == "" || (request.Body.Name == nil && request.Body.Description == nil) {
		return api.Response{}, badRequest("skillId and at least one display field are required")
	}
	principal, err := handler.resolvePrincipal(request.Context(), request.Principal)
	if err != nil {
		return api.Response{}, err
	}
	result, err := handler.skills.Update(request.Context(), principal, skillID, AgentSkillUpdateInput{
		Name: request.Body.Name, Description: request.Body.Description,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(result), nil
}

func (handler *AgentSkillHandler) Import(request api.AgentScopedRequest) (api.Response, error) {
	if request.Gin == nil || request.Gin.Request == nil {
		return api.Response{}, errors.New("Skill import request is unavailable")
	}
	skillID := strings.TrimSpace(request.Gin.Query("skillId"))
	if skillID == "" {
		return api.Response{}, badRequest("skillId is required")
	}
	contentType := strings.TrimSpace(request.Gin.GetHeader("Content-Type"))
	mediaType, _, err := mime.ParseMediaType(contentType)
	if err != nil || mediaType != "multipart/form-data" {
		return api.Response{}, badRequest("Skill import requires multipart/form-data")
	}
	principal, err := handler.resolvePrincipal(request.Context(), request.Principal)
	if err != nil {
		return api.Response{}, err
	}
	result, err := handler.skills.Import(
		request.Context(), principal, skillID, contentType, request.Gin.Request.Body,
	)
	if err != nil {
		return api.Response{}, err
	}
	if result.ImportedSkills == nil {
		result.ImportedSkills = []dto.SkillRepositoryDetectedSkill{}
	}
	return api.OK(result), nil
}

func (handler *AgentSkillHandler) Delete(
	request api.AgentScopedJSON[agentSkillDeleteRequest],
) (api.Response, error) {
	if handler == nil || handler.approvals == nil {
		return api.Response{}, errors.New("Skill delete Approval runtime is not configured")
	}
	requestID := strings.TrimSpace(request.Body.RequestID)
	skillID := strings.TrimSpace(request.Body.SkillID)
	if requestID == "" || len(requestID) > 128 || skillID == "" {
		return api.Response{}, badRequest("requestId and skillId are required")
	}
	result, err := handler.approvals.SubmitRequest(
		request.Context(), approvalapp.SkillDeleteRequestType,
		approvalapp.SubmitCommand{
			Principal: approvalapp.SubjectPrincipal{
				ActorType: "agent", WorkspaceID: request.Principal.WorkspaceID,
				PrincipalID: request.Principal.AgentID, MemberID: request.Principal.MemberID,
				MachineID: request.Principal.MachineID,
			},
			ClientRequestID: requestID,
			Payload: approvalapp.SkillDeleteRequest{
				SkillID: skillID,
			},
			CorrelationID: strings.TrimSpace(request.Gin.GetHeader("X-Request-ID")),
		},
	)
	if err != nil {
		return api.Response{}, err
	}
	public, ok := result.Value.(approvalapp.SkillDeleteResult)
	if !ok || public.ApprovalID == "" || public.ApprovalID != result.Instance.ID {
		return api.Response{}, errors.New("Skill delete Approval returned an invalid result")
	}
	switch public.Status {
	case approvalapp.SkillDeletePending:
		if _, err := handler.approvals.PresentPending(request.Context(), result); err != nil {
			return api.Response{}, err
		}
	case approvalapp.SkillDeleteRejected, approvalapp.SkillDeleteDeleted:
	default:
		return api.Response{}, errors.New("Skill delete Approval returned an invalid status")
	}
	return api.OK(agentSkillDeleteResponse{
		ApprovalID: public.ApprovalID,
		Status:     public.Status,
	}), nil
}

func parseAgentSkillPositiveInt(raw, field string, maximum int) (int, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return 0, nil
	}
	value, err := strconv.Atoi(raw)
	if err != nil || value <= 0 || (maximum > 0 && value > maximum) {
		return 0, badRequest(field + " is invalid")
	}
	return value, nil
}
