package chat

import (
	"context"
	"log/slog"

	"github.com/google/uuid"

	"github.com/yan1h/agent-coding-workflow/internal/audit"
	"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
)

type templateService struct {
	repo    Repository
	auditor audit.Recorder
	log     *slog.Logger
}

var _ TemplateService = (*templateService)(nil)

// NewTemplateService 构造 prompt_templates CRUD 服务。
func NewTemplateService(repo Repository, a audit.Recorder, log *slog.Logger) TemplateService {
	return &templateService{repo: repo, auditor: a, log: log}
}

func (s *templateService) List(ctx context.Context, userID uuid.UUID) ([]PromptTemplate, error) {
	return s.repo.ListPromptTemplatesForUser(ctx, userID)
}

func (s *templateService) Create(ctx context.Context, userID uuid.UUID, name, content string, scope TemplateScope) (*PromptTemplate, error) {
	if scope != TemplateScopeSystem && scope != TemplateScopeUser {
		return nil, errs.New(errs.CodeInvalidInput, "invalid scope")
	}
	params := InsertTemplateParams{
		ID:      uuid.Must(uuid.NewV7()),
		Name:    name,
		Content: content,
		Scope:   scope,
	}
	if scope == TemplateScopeUser {
		uid := userID
		params.OwnerID = &uid
	}
	t, err := s.repo.InsertPromptTemplate(ctx, params)
	if err != nil {
		return nil, err
	}
	if scope == TemplateScopeSystem {
		s.tryAudit(ctx, audit.Entry{
			UserID: &userID, Action: "prompt_template.created",
			TargetType: "prompt_template", TargetID: t.ID.String(),
		})
	}
	return t, nil
}

func (s *templateService) Update(ctx context.Context, userID uuid.UUID, isAdmin bool, id uuid.UUID, name, content string) error {
	t, err := s.repo.GetPromptTemplate(ctx, id)
	if err != nil {
		return err
	}
	if t == nil {
		return errs.New(errs.CodeChatTemplateNotFound, "template not found")
	}
	if err := checkTemplateWriteAccess(t, userID, isAdmin); err != nil {
		return err
	}
	if err := s.repo.UpdatePromptTemplate(ctx, id, name, content); err != nil {
		return err
	}
	if t.Scope == TemplateScopeSystem {
		s.tryAudit(ctx, audit.Entry{
			UserID: &userID, Action: "prompt_template.updated",
			TargetType: "prompt_template", TargetID: id.String(),
		})
	}
	return nil
}

func (s *templateService) Delete(ctx context.Context, userID uuid.UUID, isAdmin bool, id uuid.UUID) error {
	t, err := s.repo.GetPromptTemplate(ctx, id)
	if err != nil {
		return err
	}
	if t == nil {
		return errs.New(errs.CodeChatTemplateNotFound, "template not found")
	}
	if err := checkTemplateWriteAccess(t, userID, isAdmin); err != nil {
		return err
	}
	if err := s.repo.DeletePromptTemplate(ctx, id); err != nil {
		return err
	}
	if t.Scope == TemplateScopeSystem {
		s.tryAudit(ctx, audit.Entry{
			UserID: &userID, Action: "prompt_template.deleted",
			TargetType: "prompt_template", TargetID: id.String(),
		})
	}
	return nil
}

// checkTemplateWriteAccess 是 Update/Delete 共用的写权限判定：
// user 模板仅 owner；system 模板仅 admin。
func checkTemplateWriteAccess(t *PromptTemplate, userID uuid.UUID, isAdmin bool) error {
	if t.Scope == TemplateScopeUser && (t.OwnerID == nil || *t.OwnerID != userID) {
		return errs.New(errs.CodeForbidden, "not template owner")
	}
	if t.Scope == TemplateScopeSystem && !isAdmin {
		return errs.New(errs.CodeForbidden, "admin only: system-scoped template")
	}
	return nil
}

func (s *templateService) tryAudit(ctx context.Context, e audit.Entry) {
	if err := s.auditor.Record(ctx, e); err != nil {
		s.log.Warn("audit record failed", "action", e.Action, "err", err)
	}
}
