package mcp

import (
	"context"
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

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

// ===== fakes =====

// fakeRunSvc implements RunService.
type fakeRunSvc struct {
	profiles []run.RunProfileResp
	lastOp   string
	logs     []run.LogLineResp
}

func (f *fakeRunSvc) List(_ context.Context, _ workspace.Caller, wsID uuid.UUID) ([]run.RunProfileResp, error) {
	out := make([]run.RunProfileResp, 0, len(f.profiles))
	for _, p := range f.profiles {
		if p.WorkspaceID == wsID.String() {
			out = append(out, p)
		}
	}
	return out, nil
}
func (f *fakeRunSvc) Create(_ context.Context, _ workspace.Caller, wsID uuid.UUID, req run.CreateRunProfileReq) (run.RunProfileResp, error) {
	p := run.RunProfileResp{
		ID: uuid.NewString(), WorkspaceID: wsID.String(),
		Slug: req.Slug, Name: req.Name, Command: req.Command,
		Status: string(run.StatusStopped),
	}
	f.profiles = append(f.profiles, p)
	return p, nil
}
func (f *fakeRunSvc) Update(_ context.Context, _ workspace.Caller, profileID uuid.UUID, req run.UpdateRunProfileReq) (run.RunProfileResp, error) {
	for i := range f.profiles {
		if f.profiles[i].ID == profileID.String() {
			if req.Name != nil {
				f.profiles[i].Name = *req.Name
			}
			return f.profiles[i], nil
		}
	}
	return run.RunProfileResp{}, errs.New(errs.CodeNotFound, "profile")
}
func (f *fakeRunSvc) Delete(_ context.Context, _ workspace.Caller, profileID uuid.UUID) error {
	kept := f.profiles[:0]
	for _, p := range f.profiles {
		if p.ID != profileID.String() {
			kept = append(kept, p)
		}
	}
	f.profiles = kept
	return nil
}
func (f *fakeRunSvc) Start(_ context.Context, _ workspace.Caller, _ uuid.UUID) (run.RunStatusResp, error) {
	f.lastOp = "start"
	return run.RunStatusResp{Status: string(run.StatusRunning)}, nil
}
func (f *fakeRunSvc) Stop(_ context.Context, _ workspace.Caller, _ uuid.UUID) (run.RunStatusResp, error) {
	f.lastOp = "stop"
	return run.RunStatusResp{Status: string(run.StatusStopped)}, nil
}
func (f *fakeRunSvc) Restart(_ context.Context, _ workspace.Caller, _ uuid.UUID) (run.RunStatusResp, error) {
	f.lastOp = "restart"
	return run.RunStatusResp{Status: string(run.StatusRunning)}, nil
}
func (f *fakeRunSvc) Status(_ context.Context, _ workspace.Caller, _ uuid.UUID) (run.RunStatusResp, error) {
	f.lastOp = "status"
	return run.RunStatusResp{Status: string(run.StatusRunning)}, nil
}
func (f *fakeRunSvc) Logs(_ context.Context, _ workspace.Caller, _ uuid.UUID, _ int64, limit int) ([]run.LogLineResp, error) {
	if len(f.logs) > limit {
		return f.logs[:limit], nil
	}
	return f.logs, nil
}

func runTestDeps() (ServerDeps, *fakeRunSvc) {
	deps := testDeps()
	deps.Workspaces = &fakeWorkspaceSvc{items: []*workspace.Workspace{{
		ID: testWSID, ProjectID: testPID, Slug: "ws-1", Name: "Workspace 1",
		DefaultBranch: "main", SyncStatus: workspace.SyncStatusIdle, CreatedAt: time.Now(),
	}}}
	runSvc := &fakeRunSvc{}
	deps.Runs = runSvc
	return deps, runSvc
}

// ===== tests =====

func TestRunProfileCRUD(t *testing.T) {
	deps, runSvc := runTestDeps()

	result, err := callTool(ctxWithAuth(Scope{}), deps, "create_run_profile", map[string]any{
		"workspace_id": testWSID.String(), "slug": "dev", "name": "Dev", "command": "pnpm dev",
	})
	require.NoError(t, err)
	require.False(t, result.IsError)
	var createOut runProfileOut
	unmarshalStructured(t, result, &createOut)
	assert.True(t, createOut.OK)
	assert.Equal(t, "dev", createOut.Profile.Slug)
	pid := createOut.Profile.ID

	result, err = callTool(ctxWithAuth(Scope{}), deps, "list_run_profiles", map[string]any{
		"workspace_id": testWSID.String(),
	})
	require.NoError(t, err)
	var listOut listRunProfilesOut
	unmarshalStructured(t, result, &listOut)
	require.Len(t, listOut.Profiles, 1)

	result, err = callTool(ctxWithAuth(Scope{}), deps, "update_run_profile", map[string]any{
		"workspace_id": testWSID.String(), "profile_id": pid, "name": "Dev2",
	})
	require.NoError(t, err)
	unmarshalStructured(t, result, &createOut)
	assert.Equal(t, "Dev2", createOut.Profile.Name)

	result, err = callTool(ctxWithAuth(Scope{}), deps, "delete_run_profile", map[string]any{
		"workspace_id": testWSID.String(), "profile_id": pid,
	})
	require.NoError(t, err)
	require.False(t, result.IsError)
	assert.Empty(t, runSvc.profiles)
}

func TestRunProfileLifecycleOps(t *testing.T) {
	deps, runSvc := runTestDeps()
	p, err := runSvc.Create(context.Background(), workspace.Caller{}, testWSID, run.CreateRunProfileReq{
		Slug: "dev", Name: "Dev", Command: "pnpm dev",
	})
	require.NoError(t, err)

	for _, tc := range []struct{ tool, op, wantStatus string }{
		{"start_run_profile", "start", string(run.StatusRunning)},
		{"stop_run_profile", "stop", string(run.StatusStopped)},
		{"restart_run_profile", "restart", string(run.StatusRunning)},
		{"get_run_status", "status", string(run.StatusRunning)},
	} {
		result, err := callTool(ctxWithAuth(Scope{}), deps, tc.tool, map[string]any{
			"workspace_id": testWSID.String(), "profile_id": p.ID,
		})
		require.NoError(t, err, tc.tool)
		require.False(t, result.IsError, tc.tool)
		var out runStatusOut
		unmarshalStructured(t, result, &out)
		assert.Equal(t, tc.op, runSvc.lastOp, tc.tool)
		assert.Equal(t, tc.wantStatus, out.Status.Status, tc.tool)
	}
}

func TestRunProfileMembershipEnforced(t *testing.T) {
	deps, runSvc := runTestDeps()
	// profile 属于其他 workspace → 不可经本 workspace 操作
	runSvc.profiles = append(runSvc.profiles, run.RunProfileResp{
		ID: uuid.NewString(), WorkspaceID: uuid.NewString(), Slug: "alien", Name: "Alien",
	})

	result, err := callTool(ctxWithAuth(Scope{}), deps, "start_run_profile", map[string]any{
		"workspace_id": testWSID.String(), "profile_id": runSvc.profiles[0].ID,
	})
	require.NoError(t, err)
	assert.True(t, result.IsError)
	assert.Empty(t, runSvc.lastOp)
}

func TestGetRunLogs(t *testing.T) {
	deps, runSvc := runTestDeps()
	p, err := runSvc.Create(context.Background(), workspace.Caller{}, testWSID, run.CreateRunProfileReq{
		Slug: "dev", Name: "Dev", Command: "pnpm dev",
	})
	require.NoError(t, err)
	runSvc.logs = []run.LogLineResp{
		{ID: 1, Level: "out", Text: "listening on :3000", TS: "2026-06-10T00:00:00Z"},
		{ID: 2, Level: "err", Text: "warn: deprecated", TS: "2026-06-10T00:00:01Z"},
	}

	result, err := callTool(ctxWithAuth(Scope{}), deps, "get_run_logs", map[string]any{
		"workspace_id": testWSID.String(), "profile_id": p.ID,
	})
	require.NoError(t, err)
	require.False(t, result.IsError)
	var out getRunLogsOut
	unmarshalStructured(t, result, &out)
	require.Len(t, out.Lines, 2)
	assert.Equal(t, "listening on :3000", out.Lines[0].Text)
}
