package app

import (
	"context"
	"strconv"

	"github.com/google/uuid"

	"github.com/yan1h/agent-coding-workflow/internal/acp"
	"github.com/yan1h/agent-coding-workflow/internal/audit"
	"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
	"github.com/yan1h/agent-coding-workflow/internal/infra/notify"
	"github.com/yan1h/agent-coding-workflow/internal/orchestrator"
	"github.com/yan1h/agent-coding-workflow/internal/project"
	"github.com/yan1h/agent-coding-workflow/internal/workspace"
)

// orchPMAdapter 把 project 的 Repository + RequirementService 适配为 orchestrator.PMAccess。
// 读走 Repository（无需 Caller 鉴权——编排器内部以 run.owner 为权威身份）；ChangePhase
// 走 RequirementService 以复用阶段网关/审计。
type orchPMAdapter struct {
	repo        project.Repository
	reqs        project.RequirementService
	adminLookup userAdminAdapter
}

func (a orchPMAdapter) GetProjectByID(ctx context.Context, id uuid.UUID) (*project.Project, error) {
	return a.repo.GetProjectByID(ctx, id)
}

func (a orchPMAdapter) GetProjectBySlug(ctx context.Context, slug string) (*project.Project, error) {
	return a.repo.GetProjectBySlug(ctx, slug)
}

func (a orchPMAdapter) GetRequirementByID(ctx context.Context, id uuid.UUID) (*project.Requirement, error) {
	return a.repo.GetRequirementByID(ctx, id)
}

func (a orchPMAdapter) GetRequirementByNumber(ctx context.Context, projectID uuid.UUID, number int) (*project.Requirement, error) {
	return a.repo.GetRequirementByNumber(ctx, projectID, number)
}

// ListLatestArtifacts 取某需求各阶段最新产物（每阶段最大版本），供 BuildPrompt 注入。
func (a orchPMAdapter) ListLatestArtifacts(ctx context.Context, requirementID uuid.UUID) ([]*project.Artifact, error) {
	all, err := a.repo.ListArtifactsByRequirement(ctx, requirementID, "")
	if err != nil {
		return nil, err
	}
	latest := map[project.Phase]*project.Artifact{}
	for _, art := range all {
		if art == nil {
			continue
		}
		if cur, ok := latest[art.Phase]; !ok || art.Version > cur.Version {
			latest[art.Phase] = art
		}
	}
	out := make([]*project.Artifact, 0, len(latest))
	for _, ph := range project.AllPhases {
		if art, ok := latest[ph]; ok {
			out = append(out, art)
		}
	}
	return out, nil
}

// ChangeRequirementPhase 以 run.owner 身份推进需求阶段（经 RequirementService 复用网关/审计）。
func (a orchPMAdapter) ChangeRequirementPhase(ctx context.Context, ownerID uuid.UUID, isAdmin bool, projectSlug string, number int, to project.Phase) error {
	_, err := a.reqs.ChangePhase(ctx, project.Caller{UserID: ownerID, IsAdmin: isAdmin}, projectSlug, number, to)
	return err
}

// orchACLAdapter 把 project ACL 适配为 orchestrator.ProjectACL（owner+admin 可写）。
type orchACLAdapter struct {
	pa projectAccessAdapter
}

func (a orchACLAdapter) CanRead(ctx context.Context, userID uuid.UUID, isAdmin bool, projectID uuid.UUID) (bool, error) {
	canRead, _, err := a.pa.ResolveByID(ctx, userID, isAdmin, projectID)
	if err != nil {
		return false, err
	}
	return canRead, nil
}

func (a orchACLAdapter) CanWrite(ctx context.Context, userID uuid.UUID, isAdmin bool, projectID uuid.UUID) (bool, error) {
	_, canWrite, err := a.pa.ResolveByID(ctx, userID, isAdmin, projectID)
	if err != nil {
		return false, err
	}
	return canWrite, nil
}

// orchAuditAdapter 把 audit.Recorder 适配为 orchestrator.auditRecorder（窄 record 方法）。
// 注：orchestrator.auditRecorder 是非导出接口，但 app 在同模块外通过结构方法匹配满足。
type orchAuditAdapter struct {
	rec audit.Recorder
}

func (a orchAuditAdapter) Record(ctx context.Context, userID uuid.UUID, action, targetID string, meta map[string]any) {
	if a.rec == nil {
		return
	}
	uid := userID
	_ = a.rec.Record(ctx, audit.Entry{
		UserID: &uid, Action: action, TargetType: "orchestrator_run", TargetID: targetID, Metadata: meta,
	})
}

// orchSessionTermAdapter 把 acp.SessionService 适配为 orchestrator.sessionTerminator。
type orchSessionTermAdapter struct {
	sess acp.SessionService
}

func (a orchSessionTermAdapter) TerminateSession(ctx context.Context, ownerID uuid.UUID, isAdmin bool, sessionID uuid.UUID) error {
	return a.sess.Terminate(ctx, acp.Caller{UserID: ownerID, IsAdmin: isAdmin}, sessionID)
}

// orchGateArtifactAdapter 把 project.Repository 适配为 orchestrator.GateArtifactAccess。
type orchGateArtifactAdapter struct {
	repo project.Repository
}

func (a orchGateArtifactAdapter) ListArtifacts(ctx context.Context, projectID, requirementID uuid.UUID, phase project.Phase) ([]*project.Artifact, error) {
	return a.repo.ListArtifactsByRequirement(ctx, requirementID, phase)
}

// orchGateIssueAdapter 把 project.Repository 适配为 orchestrator.GateIssueAccess。
type orchGateIssueAdapter struct {
	repo project.Repository
}

func (a orchGateIssueAdapter) CountOpenIssues(ctx context.Context, requirementID uuid.UUID) (int, error) {
	issues, err := a.repo.ListIssuesByRequirement(ctx, requirementID)
	if err != nil {
		return 0, err
	}
	n := 0
	for _, iss := range issues {
		if iss != nil && iss.Status == project.StatusOpen {
			n++
		}
	}
	return n, nil
}

// orchACPLookupAdapter 把 acp.Repository 适配为 orchestrator.ACPSessionLookup。
type orchACPLookupAdapter struct {
	repo acp.Repository
}

func (a orchACPLookupAdapter) GetSessionByStepID(ctx context.Context, stepID uuid.UUID) (*acp.Session, error) {
	return a.repo.GetSessionByStepID(ctx, stepID)
}

func (a orchACPLookupAdapter) GetCrashedSessionForResume(ctx context.Context, stepID uuid.UUID) (*acp.Session, error) {
	return a.repo.GetCrashedSessionForResume(ctx, stepID)
}

// 确保 acp.SessionService 同时满足 orchestrator.ACPSessions（Create）。
var _ orchestrator.ACPSessions = (acp.SessionService)(nil)

// ===== 任务分解并行调度器适配器（autonomy roadmap §10）=====

// schedulerReadySourceAdapter 把 project.Repository 适配为 orchestrator.ReadyTaskSource。
type schedulerReadySourceAdapter struct {
	repo project.Repository
}

func (a schedulerReadySourceAdapter) ListSchedulableProjects(ctx context.Context) ([]uuid.UUID, error) {
	return a.repo.ListSchedulableProjects(ctx)
}

func (a schedulerReadySourceAdapter) ListReadyLeafSubtasks(ctx context.Context, projectID uuid.UUID, limit int) ([]*project.Issue, error) {
	return a.repo.ListReadyLeafSubtasks(ctx, projectID, limit)
}

func (a schedulerReadySourceAdapter) CountActiveSessionsForProject(ctx context.Context, projectID uuid.UUID) (int, error) {
	return a.repo.CountActiveSessionsForProject(ctx, projectID)
}

var _ orchestrator.ReadyTaskSource = schedulerReadySourceAdapter{}

// schedulerSessionStarterAdapter 为就绪子任务以特权身份（项目 owner）创建 ACP session。
// 解析 workspace（issue.WorkspaceID 优先，否则取项目首个 workspace）与 agent-kind
// （implementing 阶段默认），把 IssueNumber 注入使 SessionService 绑定 issue/<slug>-N worktree。
type schedulerSessionStarterAdapter struct {
	sess             acp.SessionService
	projectRepo      project.Repository
	wsRepo           workspace.Repository
	defaultAgentKind func() (uuid.UUID, bool) // 解析 implementing 阶段默认 agent-kind
}

func (a schedulerSessionStarterAdapter) StartForIssue(ctx context.Context, iss *project.Issue) (uuid.UUID, error) {
	proj, err := a.projectRepo.GetProjectByID(ctx, iss.ProjectID)
	if err != nil {
		return uuid.Nil, err
	}
	// 解析 workspace：issue 显式关联优先，否则取项目首个 workspace。
	var wsID uuid.UUID
	if iss.WorkspaceID != nil {
		wsID = *iss.WorkspaceID
	} else {
		list, lerr := a.wsRepo.ListWorkspacesByProject(ctx, iss.ProjectID)
		if lerr != nil {
			return uuid.Nil, lerr
		}
		if len(list) == 0 {
			return uuid.Nil, errs.New(errs.CodeInvalidInput, "project 无可用 workspace，无法为子任务派发 session")
		}
		wsID = list[0].ID
	}
	akID, ok := a.defaultAgentKind()
	if !ok {
		return uuid.Nil, errs.New(errs.CodeInvalidInput, "未配置 implementing 阶段默认 agent-kind，无法派发子任务 session")
	}
	num := iss.Number
	owner := acp.Caller{UserID: proj.OwnerID, IsAdmin: false}
	created, err := a.sess.Create(ctx, owner, acp.CreateSessionInput{
		WorkspaceID: wsID,
		AgentKindID: akID,
		IssueNumber: &num,
	})
	if err != nil {
		return uuid.Nil, err
	}
	return created.ID, nil
}

var _ orchestrator.SessionStarter = schedulerSessionStarterAdapter{}

// schedulerNotifyAdapter 把 notify.Dispatcher 适配为 orchestrator.SchedulerNotifier。
// 通知发给项目 owner（best-effort，失败不阻塞调度）。
type schedulerNotifyAdapter struct {
	dispatcher  *notify.Dispatcher
	projectRepo project.Repository
}

func (a schedulerNotifyAdapter) ownerOf(ctx context.Context, projectID uuid.UUID) (uuid.UUID, bool) {
	proj, err := a.projectRepo.GetProjectByID(ctx, projectID)
	if err != nil {
		return uuid.Nil, false
	}
	return proj.OwnerID, true
}

func (a schedulerNotifyAdapter) NotifyDispatched(ctx context.Context, iss *project.Issue, sessionID uuid.UUID) {
	if a.dispatcher == nil {
		return
	}
	owner, ok := a.ownerOf(ctx, iss.ProjectID)
	if !ok {
		return
	}
	_ = a.dispatcher.Dispatch(ctx, notify.Message{
		UserID:   owner,
		Topic:    "orchestrator.subtask_dispatched",
		Severity: notify.SeverityInfo,
		Title:    "子任务已派发",
		Body:     "子任务 #" + itoa(iss.Number) + " 已派发到并行 ACP session",
		Metadata: map[string]any{"issue_number": iss.Number, "session_id": sessionID.String()},
	})
}

func (a schedulerNotifyAdapter) NotifyQuotaBlocked(ctx context.Context, iss *project.Issue) {
	if a.dispatcher == nil {
		return
	}
	owner, ok := a.ownerOf(ctx, iss.ProjectID)
	if !ok {
		return
	}
	_ = a.dispatcher.Dispatch(ctx, notify.Message{
		UserID:   owner,
		Topic:    "orchestrator.subtask_quota_blocked",
		Severity: notify.SeverityWarning,
		Title:    "子任务派发受配额限制",
		Body:     "子任务 #" + itoa(iss.Number) + " 因会话配额暂缓派发，下一轮重试",
		Metadata: map[string]any{"issue_number": iss.Number},
	})
}

var _ orchestrator.SchedulerNotifier = schedulerNotifyAdapter{}

func itoa(n int) string { return strconv.Itoa(n) }
