package dto

import (
	"encoding/json"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/service/roles"
)

type RoleCapabilities struct {
	Enabled                   bool   `json:"enabled"`
	PresetRoles               bool   `json:"presetRoles"`
	MyRoles                   bool   `json:"myRoles"`
	Realization               bool   `json:"realization"`
	RolePackage               bool   `json:"rolePackage"`
	RolePackageDisabledReason string `json:"rolePackageDisabledReason,omitempty"`
	DisabledReason            string `json:"disabledReason,omitempty"`
}

type RoleRuntimeHints struct {
	Runtime         string `json:"runtime,omitempty"`
	Model           string `json:"model,omitempty"`
	ReasoningEffort string `json:"reasoningEffort,omitempty"`
	SuggestedName   string `json:"suggestedName,omitempty"`
}

type RolePackageManifest struct {
	Items []RolePackageManifestItem `json:"items"`
}

type RolePackageManifestItem struct {
	Type        string `json:"type"`
	Name        string `json:"name,omitempty"`
	Description string `json:"description,omitempty"`
	Required    bool   `json:"required,omitempty"`
	Action      string `json:"action,omitempty"`
	ResourceID  string `json:"resourceId,omitempty"`
	AccessLevel string `json:"accessLevel,omitempty"`
	Branch      string `json:"branch,omitempty"`
	Content     string `json:"content,omitempty"`
}

type Role struct {
	ID                    string              `json:"id"`
	WorkspaceID           string              `json:"workspaceId,omitempty"`
	Source                string              `json:"source"`
	Visibility            string              `json:"visibility"`
	Slug                  string              `json:"slug"`
	Name                  string              `json:"name"`
	Description           string              `json:"description,omitempty"`
	Locale                string              `json:"locale,omitempty"`
	Scenario              string              `json:"scenario,omitempty"`
	Icon                  string              `json:"icon,omitempty"`
	Tags                  []string            `json:"tags,omitempty"`
	Definition            string              `json:"definition"`
	RuntimeHints          RoleRuntimeHints    `json:"runtimeHints"`
	SourcePresetID        string              `json:"sourcePresetId,omitempty"`
	SourcePresetVersion   int                 `json:"sourcePresetVersion,omitempty"`
	RoleVersionID         string              `json:"roleVersionId,omitempty"`
	SourcePresetVersionID string              `json:"sourcePresetVersionId,omitempty"`
	SourceAgentID         string              `json:"sourceAgentId,omitempty"`
	IsActive              bool                `json:"isActive"`
	Role                  string              `json:"role,omitempty"`
	CanManage             bool                `json:"canManage,omitempty"`
	OwnerID               string              `json:"ownerId,omitempty"`
	Editable              bool                `json:"editable,omitempty"`
	Provenance            RoleProvenance      `json:"provenance"`
	RolePackage           RolePackage         `json:"rolePackage"`
	RolePackageManifest   RolePackageManifest `json:"rolePackageManifest"`
	CreatedBy             string              `json:"createdBy,omitempty"`
	UpdatedBy             string              `json:"updatedBy,omitempty"`
	CreatedAt             string              `json:"createdAt,omitempty"`
	UpdatedAt             string              `json:"updatedAt,omitempty"`
}

type RoleProvenance struct {
	Kind    string `json:"kind"`
	Ref     string `json:"ref"`
	Version int    `json:"version,omitempty"`
}

type RolePackage struct {
	Portable    bool                    `json:"portable"`
	Sections    []string                `json:"sections"`
	NonPortable []string                `json:"nonPortable,omitempty"`
	Unresolved  []RolePackageUnresolved `json:"unresolved,omitempty"`
}

func (p RolePackage) MarshalJSON() ([]byte, error) {
	type alias RolePackage
	out := struct {
		alias
		Manifest RolePackageManifest `json:"manifest"`
	}{
		alias:    alias(p),
		Manifest: rolePackageManifestFromSections(p.Sections),
	}
	return json.Marshal(out)
}

type RolePackageUnresolved struct {
	Section  string `json:"section"`
	Name     string `json:"name,omitempty"`
	Reason   string `json:"reason"`
	Required bool   `json:"required,omitempty"`
}

type RoleListResponse struct {
	Items   []Role `json:"items"`
	Page    int    `json:"page"`
	PerPage int    `json:"perPage"`
	Total   int64  `json:"total"`
	HasMore bool   `json:"hasMore"`
}

type RoleVersion struct {
	ID               string `json:"id"`
	Version          int    `json:"version"`
	ContentID        string `json:"contentId"`
	ContentVersionID string `json:"contentVersionId"`
	CreatedBy        string `json:"createdBy"`
	CreatedAt        string `json:"createdAt"`
	Active           bool   `json:"active"`
}

type RoleVersionListResponse struct {
	Items   []RoleVersion `json:"items"`
	Page    int           `json:"page"`
	PerPage int           `json:"perPage"`
	HasMore bool          `json:"hasMore"`
}

type RoleVersionRollbackRequest struct {
	ExpectedRoleVersionID string `json:"expectedRoleVersionId"`
	TargetRoleVersionID   string `json:"targetRoleVersionId"`
}

type RoleMutationRequest struct {
	Name                string              `json:"name"`
	Description         string              `json:"description,omitempty"`
	Locale              string              `json:"locale,omitempty"`
	Scenario            string              `json:"scenario,omitempty"`
	Icon                string              `json:"icon,omitempty"`
	Tags                []string            `json:"tags,omitempty"`
	Definition          string              `json:"definition"`
	RuntimeHints        RoleRuntimeHints    `json:"runtimeHints,omitempty"`
	RolePackageManifest RolePackageManifest `json:"rolePackageManifest,omitempty"`
	IsActive            *bool               `json:"isActive,omitempty"`
}

type RoleVisibilityRequest struct {
	Visibility string `json:"visibility"`
}

type legacyRoleAttachment struct {
	Type        string `json:"type"`
	Name        string `json:"name,omitempty"`
	Description string `json:"description,omitempty"`
	Required    bool   `json:"required,omitempty"`
	Realization string `json:"realization,omitempty"`
	ResourceID  string `json:"resourceId,omitempty"`
	AccessLevel string `json:"accessLevel,omitempty"`
	Branch      string `json:"branch,omitempty"`
	Content     string `json:"content,omitempty"`
}

func (r *RoleMutationRequest) UnmarshalJSON(data []byte) error {
	type raw RoleMutationRequest
	aux := struct {
		raw
		Attachments []legacyRoleAttachment `json:"attachments,omitempty"`
	}{}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	*r = RoleMutationRequest(aux.raw)
	if len(r.RolePackageManifest.Items) == 0 && len(aux.Attachments) > 0 {
		r.RolePackageManifest = rolePackageManifestFromLegacyAttachments(r.Definition, aux.Attachments)
	}
	return nil
}

func (r RoleMutationRequest) Input() roles.Mutation {
	manifest := r.RolePackageManifest
	if len(manifest.Items) == 0 {
		manifest = RolePackageManifest{Items: []RolePackageManifestItem{{Type: roles.ManifestItemRoleDefinitionAsset, Name: "IDENTITY.md", Description: "Canonical role definition", Required: true, Action: "materialize", Content: r.Definition}}}
	}
	return roles.Mutation{Name: r.Name, Description: r.Description, Locale: r.Locale, Scenario: r.Scenario, Icon: r.Icon, Tags: r.Tags, Definition: r.Definition, RuntimeHints: roleRuntimeHintsInput(r.RuntimeHints), Attachments: manifestItemsToAttachments(manifest.Items), IsActive: r.IsActive}
}

type AgentDerivedRoleMutationRequest struct {
	AgentID string `json:"agentId"`
	RoleMutationRequest
}

func (r *AgentDerivedRoleMutationRequest) UnmarshalJSON(data []byte) error {
	agent := struct {
		AgentID string `json:"agentId"`
	}{}
	if err := json.Unmarshal(data, &agent); err != nil {
		return err
	}
	var role RoleMutationRequest
	if err := json.Unmarshal(data, &role); err != nil {
		return err
	}
	r.AgentID = agent.AgentID
	r.RoleMutationRequest = role
	return nil
}

func (r AgentDerivedRoleMutationRequest) Input() roles.AgentDerivedMutation {
	return roles.AgentDerivedMutation{AgentID: r.AgentID, Mutation: r.RoleMutationRequest.Input()}
}

type AddPresetRoleRequest struct {
	PresetID    string `json:"presetId"`
	Locale      string `json:"locale,omitempty"`
	DisplayName string `json:"displayName,omitempty"`
}

func (r AddPresetRoleRequest) Input() roles.AddPresetRequest {
	return roles.AddPresetRequest{PresetID: r.PresetID, Locale: r.Locale, DisplayName: r.DisplayName}
}

type RoleRealizationRequest struct {
	AgentID        string `json:"agentId"`
	RoleVersionID  string `json:"roleVersionId,omitempty"`
	DryRun         bool   `json:"dryRun,omitempty"`
	Apply          bool   `json:"apply,omitempty"`
	SkipOptional   bool   `json:"skipOptional,omitempty"`
	StorageScope   string `json:"storageScope,omitempty"`
	TemplateLocale string `json:"templateLocale,omitempty"`
}

func (r RoleRealizationRequest) Input() roles.RealizationRequest {
	return roles.RealizationRequest{AgentID: r.AgentID, RoleVersionID: r.RoleVersionID, DryRun: r.DryRun, Apply: r.Apply, SkipOptional: r.SkipOptional, StorageScope: r.StorageScope, TemplateLocale: r.TemplateLocale}
}

type PresetRoleRealizationRequest struct {
	PresetID       string `json:"presetId"`
	Locale         string `json:"locale,omitempty"`
	DisplayName    string `json:"displayName,omitempty"`
	AgentID        string `json:"agentId"`
	DryRun         bool   `json:"dryRun,omitempty"`
	Apply          bool   `json:"apply,omitempty"`
	SkipOptional   bool   `json:"skipOptional,omitempty"`
	StorageScope   string `json:"storageScope,omitempty"`
	TemplateLocale string `json:"templateLocale,omitempty"`
}

func (r PresetRoleRealizationRequest) PresetInput() roles.AddPresetRequest {
	return roles.AddPresetRequest{PresetID: r.PresetID, Locale: r.Locale, DisplayName: r.DisplayName}
}

func (r PresetRoleRealizationRequest) RealizationInput() roles.RealizationRequest {
	return roles.RealizationRequest{AgentID: r.AgentID, DryRun: r.DryRun, Apply: r.Apply, SkipOptional: r.SkipOptional, StorageScope: r.StorageScope, TemplateLocale: r.TemplateLocale}
}

type RoleRealizationItem struct {
	ManifestItemType string `json:"manifestItemType"`
	ManifestItemName string `json:"manifestItemName,omitempty"`
	Action           string `json:"action"`
	Status           string `json:"status"`
	ResourceID       string `json:"resourceId,omitempty"`
	AccessLevel      string `json:"accessLevel,omitempty"`
	Branch           string `json:"branch,omitempty"`
	Message          string `json:"message,omitempty"`
	Required         bool   `json:"required,omitempty"`
}

type RoleRealizationResponse struct {
	RoleID  string                `json:"roleId"`
	AgentID string                `json:"agentId"`
	DryRun  bool                  `json:"dryRun"`
	Apply   bool                  `json:"apply"`
	Items   []RoleRealizationItem `json:"items"`
}

type PresetRoleRealizationResponse struct {
	Role        Role                    `json:"role"`
	Realization RoleRealizationResponse `json:"realization"`
}

type RoleRealizationImpactResponse struct {
	ManagedMemories   int64 `json:"managedMemories"`
	SkillRepositories int64 `json:"skillRepositories"`
}

func roleRuntimeHintsInput(hints RoleRuntimeHints) roles.RuntimeHints {
	return roles.RuntimeHints{Runtime: hints.Runtime, Model: hints.Model, ReasoningEffort: hints.ReasoningEffort, SuggestedName: hints.SuggestedName}
}

func manifestItemsToAttachments(items []RolePackageManifestItem) []roles.Attachment {
	out := make([]roles.Attachment, 0, len(items))
	for _, item := range items {
		switch strings.TrimSpace(item.Type) {
		case "", roles.AttachmentRoleDefinition, roles.ManifestItemRoleDefinitionAsset:
			continue
		}
		out = append(out, roles.Attachment{Type: item.Type, Name: item.Name, Description: item.Description, Required: item.Required, Realization: item.Action, ResourceID: item.ResourceID, AccessLevel: item.AccessLevel, Branch: item.Branch, Content: item.Content})
	}
	return out
}

func rolePackageManifestFromLegacyAttachments(definition string, attachments []legacyRoleAttachment) RolePackageManifest {
	items := []RolePackageManifestItem{{Type: roles.ManifestItemRoleDefinitionAsset, Name: "IDENTITY.md", Description: "Canonical role definition", Required: true, Action: "materialize", Content: definition}}
	for _, item := range attachments {
		items = append(items, RolePackageManifestItem{Type: item.Type, Name: item.Name, Description: item.Description, Required: item.Required, Action: item.Realization, ResourceID: item.ResourceID, AccessLevel: item.AccessLevel, Branch: item.Branch, Content: item.Content})
	}
	return RolePackageManifest{Items: items}
}

func rolePackageManifestFromSections(sections []string) RolePackageManifest {
	items := make([]RolePackageManifestItem, 0, len(sections))
	for _, section := range sections {
		section = strings.TrimSpace(section)
		if section == "" {
			continue
		}
		if section == roles.AttachmentRoleDefinition {
			section = roles.ManifestItemRoleDefinitionAsset
		}
		items = append(items, RolePackageManifestItem{Type: section})
	}
	return RolePackageManifest{Items: items}
}
