// Package project 内的 artifact_service.go 实装 ArtifactService。
//
// 设计取舍：
//   - Version 在 (Requirement, Phase) 内自增（max+1），由 service 计算后落库；
//     UNIQUE(requirement_id, phase, version) 兜底竞争，Create 捕获 unique_violation 重试 1 次。
//   - Phase 仅允许 ArtifactPhases（planning/prototyping/auditing/implementing/reviewing），与 DB CHECK 一致。
//   - Approve 给某版本打 verdict（pass/fail）；verdict=pass 时把阶段指针指向该产物，
//     供 RequirementService 的阶段网关放行后续 phase。
//   - 已 closed requirement：禁止新建产物版本，返回 CodeRequirementClosed。
//   - 已归档 project：所有写操作经 loadProjectForWrite 拒绝。
//   - 读操作经 loadProjectForRead 校验可见性。
package project

import (
	"context"

	"github.com/google/uuid"

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

type artifactService struct {
	repo  Repository
	audit audit.Recorder
}

// NewArtifactService 构造 ArtifactService。
func NewArtifactService(repo Repository, rec audit.Recorder) ArtifactService {
	return &artifactService{repo: repo, audit: rec}
}

func (s *artifactService) Create(ctx context.Context, c Caller, slug string, reqNumber int, in CreateArtifactInput) (*Artifact, error) {
	if !in.Phase.IsArtifactPhase() {
		return nil, errs.New(errs.CodeInvalidInput, "phase 必须是 planning/prototyping/auditing/implementing/reviewing 之一")
	}
	if in.Content == "" {
		return nil, errs.New(errs.CodeInvalidInput, "content 必填")
	}
	p, err := loadProjectForWrite(ctx, s.repo, c, slug)
	if err != nil {
		return nil, err
	}
	r, err := s.repo.GetRequirementByNumber(ctx, p.ID, reqNumber)
	if err != nil {
		return nil, err
	}
	if r.Status == StatusClosed {
		return nil, errs.New(errs.CodeRequirementClosed, "已关闭需求禁止新增产物版本")
	}
	out, err := s.createWithRetry(ctx, r.ID, c.UserID, in)
	if err != nil {
		return nil, err
	}
	s.recordAudit(ctx, c, "requirement_artifact.create", out.ID.String(), map[string]any{
		"requirement_id": r.ID.String(), "phase": string(out.Phase), "version": out.Version,
	})
	return out, nil
}

// createWithRetry 取 (requirement, phase) 内 max+1 作为新 version；version 撞 UNIQUE 时重试 1 次。
func (s *artifactService) createWithRetry(ctx context.Context, reqID, createdBy uuid.UUID, in CreateArtifactInput) (*Artifact, error) {
	for attempt := 0; attempt < 2; attempt++ {
		maxVer, err := s.repo.GetMaxArtifactVersion(ctx, reqID, in.Phase)
		if err != nil {
			return nil, err
		}
		out, err := s.repo.CreateArtifact(ctx, &Artifact{
			ID: uuid.New(), RequirementID: reqID, Phase: in.Phase,
			Version: maxVer + 1, Content: in.Content, Note: in.Note,
			SourceMessageID: in.SourceMessageID,
			CreatedBy:       createdBy,
		})
		if err == nil {
			return out, nil
		}
		if !IsUniqueViolation(err) {
			return nil, err
		}
		// version 撞了，下一轮重新取 max
	}
	return nil, errs.New(errs.CodeInternal, "artifact version 持续冲突")
}

func (s *artifactService) List(ctx context.Context, c Caller, slug string, reqNumber int, phase Phase) ([]*Artifact, error) {
	if phase != "" && !phase.IsArtifactPhase() {
		return nil, errs.New(errs.CodeInvalidInput, "phase 必须是 planning/prototyping/auditing/implementing/reviewing 之一")
	}
	p, err := loadProjectForRead(ctx, s.repo, c, slug)
	if err != nil {
		return nil, err
	}
	r, err := s.repo.GetRequirementByNumber(ctx, p.ID, reqNumber)
	if err != nil {
		return nil, err
	}
	return s.repo.ListArtifactsByRequirement(ctx, r.ID, phase)
}

func (s *artifactService) GetByVersion(ctx context.Context, c Caller, slug string, reqNumber int, phase Phase, version int) (*Artifact, error) {
	if !phase.IsArtifactPhase() {
		return nil, errs.New(errs.CodeInvalidInput, "phase 必须是 planning/prototyping/auditing/implementing/reviewing 之一")
	}
	p, err := loadProjectForRead(ctx, s.repo, c, slug)
	if err != nil {
		return nil, err
	}
	r, err := s.repo.GetRequirementByNumber(ctx, p.ID, reqNumber)
	if err != nil {
		return nil, err
	}
	return s.repo.GetArtifactByVersion(ctx, r.ID, phase, version)
}

func (s *artifactService) Approve(ctx context.Context, c Caller, slug string, reqNumber int, phase Phase, version int, verdict Verdict) (*Artifact, error) {
	if !phase.IsArtifactPhase() {
		return nil, errs.New(errs.CodeInvalidInput, "phase 必须是 planning/prototyping/auditing/implementing/reviewing 之一")
	}
	if verdict != VerdictPass && verdict != VerdictFail {
		return nil, errs.New(errs.CodeInvalidInput, "verdict 必须是 pass 或 fail")
	}
	p, err := loadProjectForWrite(ctx, s.repo, c, slug)
	if err != nil {
		return nil, err
	}
	r, err := s.repo.GetRequirementByNumber(ctx, p.ID, reqNumber)
	if err != nil {
		return nil, err
	}
	if r.Status == StatusClosed {
		return nil, errs.New(errs.CodeRequirementClosed, "已关闭需求禁止批准产物")
	}
	art, err := s.repo.GetArtifactByVersion(ctx, r.ID, phase, version)
	if err != nil {
		return nil, err
	}
	out, err := s.repo.ApproveArtifact(ctx, art.ID, verdict)
	if err != nil {
		return nil, err
	}
	// verdict=pass 才把阶段指针指向此产物（网关据此放行）；fail 仅落 verdict、不动指针。
	if verdict == VerdictPass {
		if _, err := s.repo.UpsertPhasePointer(ctx, r.ID, phase, out.ID, c.UserID); err != nil {
			return nil, err
		}
	}
	s.recordAudit(ctx, c, "requirement_artifact.approve", out.ID.String(), map[string]any{
		"requirement_id": r.ID.String(), "phase": string(phase), "version": version, "verdict": string(verdict),
	})
	return out, nil
}

func (s *artifactService) recordAudit(ctx context.Context, c Caller, action, targetID string, meta map[string]any) {
	if s.audit == nil {
		return
	}
	uid := c.UserID
	_ = s.audit.Record(context.WithoutCancel(ctx), audit.Entry{
		UserID: &uid, Action: action, TargetType: "requirement_artifact", TargetID: targetID, Metadata: meta,
	})
}
