package manage

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

func (service *Service) ValidateForOwner(ctx context.Context, ownerUserID string, workspaceID string, input UpsertInput, existingTriggerID string) (ValidateResult, error) {
	var current *TriggerView
	var existing *definitionRecord
	if strings.TrimSpace(existingTriggerID) != "" {
		route, err := service.resolveScheduleRoute(ctx, ownerUserID, workspaceID, existingTriggerID)
		if err != nil {
			return ValidateResult{}, err
		}
		owner, err := service.loadManageOwner(ctx, workspaceID, ownerUserID)
		if err != nil {
			return ValidateResult{}, err
		}
		definition, err := service.loadScheduleDefinitionForOwner(ctx, workspaceID, owner.MemberID, route.TargetID)
		if err != nil {
			return ValidateResult{}, err
		}
		record := definitionRecordFromScheduleDefinition(ownerUserID, definition)
		existing = &record
		view, err := service.viewFromScheduleRoute(ctx, ownerUserID, route, definition, true)
		if err != nil {
			return ValidateResult{}, err
		}
		current = &view
	}

	normalized, err := service.normalizeMutation(ctx, ownerUserID, workspaceID, input, existing, service.now(), false)
	if err != nil {
		return ValidateResult{}, err
	}
	proposed := triggerViewFromRecord(normalized.record, normalized.target, normalized.validation)
	proposed.NextOccurrenceAt = normalized.proposedNextAt
	if normalized.record.TimeMode == TimeModeOneShotRelative {
		proposed.ScheduledAt = nil
	}

	return ValidateResult{
		Current:              current,
		Proposed:             proposed,
		EstimatedScheduledAt: normalized.estimatedScheduledAt,
	}, nil
}

func (service *Service) viewFromDefinition(ctx context.Context, ownerUserID string, record definitionRecord, includeValidation bool) (TriggerView, error) {
	target, err := service.resolveTargetView(ctx, record.WorkspaceID, record.TargetKind, record.TargetRef, false)
	if err != nil {
		return TriggerView{}, err
	}
	view := triggerViewFromRecord(record, target, ValidationView{})
	if includeValidation {
		view.Validation = service.currentValidationForDefinition(ctx, ownerUserID, record)
	}
	return view, nil
}

func triggerViewFromRecord(record definitionRecord, target TargetView, validation ValidationView) TriggerView {
	return TriggerView{
		ID:                    record.ID,
		Name:                  record.Name,
		Status:                record.Status,
		SourceKind:            record.SourceKind,
		TimeMode:              record.TimeMode,
		EventSubscription:     eventSubscriptionView(mustParseEventSubscription(record.EventSubscription)),
		ScheduledAt:           record.ScheduledAt,
		RelativeSeconds:       record.RelativeSeconds,
		AnchorAt:              record.AnchorAt,
		IntervalSeconds:       record.IntervalSeconds,
		CronExpression:        record.CronExpression,
		Timezone:              record.Timezone,
		Target:                target,
		MessageTemplate:       record.MessageTemplate,
		MentionHints:          record.MentionHints,
		AlsoStartThread:       record.AlsoStartThread && record.TargetKind == TargetKindChannel,
		HumanNotificationMode: normalizedHumanNotificationMode(record.HumanNotificationMode),
		NextOccurrenceAt:      record.NextOccurrenceAt,
		LastRunAt:             record.LastRunAt,
		LastSuccessAt:         record.LastSuccessAt,
		LastFailureCode:       record.LastFailureCode,
		CreatedAt:             record.CreatedAt,
		UpdatedAt:             record.UpdatedAt,
		Validation:            validation,
	}
}

func (service *Service) currentValidationForDefinition(ctx context.Context, ownerUserID string, record definitionRecord) ValidationView {
	switch record.Status {
	case StatusPaused:
		return ValidationView{Valid: false, Code: string(FailureCodeTriggerNotActive), Message: "trigger is paused"}
	case StatusBlocked:
		code := strings.TrimSpace(record.LastFailureCode)
		if code == "" {
			code = string(FailureCodeTriggerNotActive)
		}
		return ValidationView{Valid: false, Code: code, Message: "trigger is blocked"}
	case StatusCompleted:
		return ValidationView{Valid: false, Code: string(FailureCodeTriggerCompleted), Message: "trigger is completed"}
	}
	validation, _ := service.validateDefinitionForOwner(ctx, ownerUserID, record)
	return validation
}

func (service *Service) validateDefinitionForOwner(ctx context.Context, ownerUserID string, record definitionRecord) (ValidationView, error) {
	owner, err := service.loadManageOwner(ctx, record.WorkspaceID, ownerUserID)
	if err != nil {
		if isForbidden(err) {
			return ValidationView{Valid: false, Code: string(FailureCodeOwnerMembershipLost), Message: err.Error()}, err
		}
		return ValidationView{Valid: false, Code: string(FailureCodeOwnerMembershipLost), Message: err.Error()}, err
	}

	if record.SourceKind == SourceKindEvent {
		err := badRequest("source_kind_not_enabled")
		return ValidationView{Valid: false, Code: string(FailureCodeSourceKindNotEnabled), Message: err.Error()}, err
	}
	contentToValidate := record.MessageTemplate
	if service == nil || service.validator == nil {
		err := fmt.Errorf("trigger message validator is not configured")
		return ValidationView{Valid: false, Code: string(FailureCodeTransientWriteError), Message: err.Error()}, err
	}
	if err := service.validator.ValidateMessageForUser(ctx, owner.User, record.WorkspaceID, record.TargetRef, contentToValidate); err != nil {
		code := string(FailureCodeContentInvalid)
		switch {
		case errors.Is(err, storeerr.ErrNotFound):
			code = string(FailureCodeTargetNotFound)
		case errors.Is(err, storeerr.ErrForbidden):
			code = string(FailureCodeForbiddenCurrentPerms)
		case errors.Is(err, storeerr.ErrBadRequest):
			code = string(FailureCodeContentInvalid)
		}
		return ValidationView{Valid: false, Code: code, Message: err.Error()}, err
	}
	return ValidationView{Valid: true}, nil
}

func mustParseEventSubscription(raw string) *normalizedEventSubscription {
	subscription, err := parseEventSubscription(raw)
	if err != nil {
		return nil
	}
	return subscription
}

type manageOwner struct {
	User          model.User
	MemberID      string
	WorkspaceRole string
}

func (service *Service) loadManageOwner(ctx context.Context, workspaceID string, ownerUserID string) (manageOwner, error) {
	row := service.db.QueryRow(ctx, `
SELECT u.id, COALESCE(u.email, ''), u.name, u.display_name, m.id,
       COALESCE(m.workspace_role, '')
FROM users u
JOIN members m ON m.user_id = u.id
WHERE u.id = $1
  AND m.workspace_id = $2
  AND m.type = 'human'
  AND m.status = 'active'
LIMIT 1
`, strings.TrimSpace(ownerUserID), strings.TrimSpace(workspaceID))

	var owner manageOwner
	if err := row.Scan(&owner.User.ID, &owner.User.Email, &owner.User.Name, &owner.User.DisplayName, &owner.MemberID, &owner.WorkspaceRole); err != nil {
		if coredb.IsNotFound(err) {
			return manageOwner{}, forbidden("owner is not a workspace member")
		}
		return manageOwner{}, fmt.Errorf("load trigger owner: %w", err)
	}
	owner.WorkspaceRole = strings.TrimSpace(owner.WorkspaceRole)
	return owner, nil
}

func isForbidden(err error) bool {
	return err != nil && errors.Is(err, storeerr.ErrForbidden)
}
