package taskboard

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

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
)

const taskBoardSavedViewNameMaxRunes = 120

func (txs TxStore) ListTaskBoardSavedViews(ctx context.Context, request ListTaskBoardSavedViewsRequest) (ListTaskBoardSavedViewsResponse, error) {
	workspaceID, memberID, err := txs.resolveTaskBoardSavedViewMember(ctx, request.Context)
	if err != nil {
		return ListTaskBoardSavedViewsResponse{}, err
	}
	savedViews, err := loadTaskBoardSavedViews(ctx, txs.db, workspaceID, memberID)
	if err != nil {
		return ListTaskBoardSavedViewsResponse{}, err
	}
	return ListTaskBoardSavedViewsResponse{SavedViews: savedViews}, nil
}

func (txs TxStore) CreateTaskBoardSavedView(ctx context.Context, request CreateTaskBoardSavedViewRequest) (CreateTaskBoardSavedViewResponse, error) {
	workspaceID, memberID, err := txs.resolveTaskBoardSavedViewMember(ctx, request.Context)
	if err != nil {
		return CreateTaskBoardSavedViewResponse{}, err
	}
	name, err := normalizeTaskBoardSavedViewName(request.Name)
	if err != nil {
		return CreateTaskBoardSavedViewResponse{}, err
	}
	normalizedState := normalizeTaskBoardViewState(request.ViewState)
	savedViewID, err := newTaskBoardSavedViewID()
	if err != nil {
		return CreateTaskBoardSavedViewResponse{}, fmt.Errorf("create task board saved view id: %w", err)
	}
	now := time.Now().UTC()
	payload, err := marshalTaskBoardViewStateJSON(normalizedState)
	if err != nil {
		return CreateTaskBoardSavedViewResponse{}, fmt.Errorf("marshal task board saved view state: %w", err)
	}
	if _, err := txs.tx.Exec(ctx, `
INSERT INTO task_board_saved_views (
    id,
    workspace_id,
    member_id,
    name,
    state_json,
    created_at,
    updated_at
) VALUES ($1, $2, $3, $4, $5, $6, $6)
`, savedViewID, workspaceID, memberID, name, string(payload), now); err != nil {
		if coredb.IsUniqueViolation(err) {
			return CreateTaskBoardSavedViewResponse{}, requestFailure(ErrConflict, "saved view name already exists")
		}
		return CreateTaskBoardSavedViewResponse{}, fmt.Errorf("insert task board saved view: %w", err)
	}
	return CreateTaskBoardSavedViewResponse{SavedView: TaskBoardSavedView{
		ID:          savedViewID,
		WorkspaceID: workspaceID,
		MemberID:    memberID,
		Name:        name,
		ViewState:   normalizedState,
		CreatedAt:   now,
		UpdatedAt:   now,
	}}, nil
}

func (txs TxStore) UpdateTaskBoardSavedView(ctx context.Context, request UpdateTaskBoardSavedViewRequest) (UpdateTaskBoardSavedViewResponse, error) {
	workspaceID, memberID, err := txs.resolveTaskBoardSavedViewMember(ctx, request.Context)
	if err != nil {
		return UpdateTaskBoardSavedViewResponse{}, err
	}
	savedViewID, err := requireTaskBoardSavedViewID(request.SavedViewID)
	if err != nil {
		return UpdateTaskBoardSavedViewResponse{}, err
	}
	name, err := normalizeTaskBoardSavedViewName(request.Name)
	if err != nil {
		return UpdateTaskBoardSavedViewResponse{}, err
	}
	existing, err := loadTaskBoardSavedView(ctx, txs.tx, workspaceID, memberID, savedViewID)
	if err != nil {
		return UpdateTaskBoardSavedViewResponse{}, err
	}
	normalizedState := normalizeTaskBoardViewState(request.ViewState)
	if request.ViewState.CardPropertyToggles == nil {
		normalizedState = preserveTaskBoardCardPropertyToggles(
			normalizedState,
			existing.ViewState,
		)
	}
	payload, err := marshalTaskBoardViewStateJSON(normalizedState)
	if err != nil {
		return UpdateTaskBoardSavedViewResponse{}, fmt.Errorf("marshal task board saved view state: %w", err)
	}
	now := time.Now().UTC()
	tag, err := txs.tx.Exec(ctx, `
UPDATE task_board_saved_views
SET name = $4,
    state_json = $5,
    updated_at = $6
WHERE workspace_id = $1
  AND member_id = $2
  AND id = $3
`, workspaceID, memberID, savedViewID, name, string(payload), now)
	if err != nil {
		if coredb.IsUniqueViolation(err) {
			return UpdateTaskBoardSavedViewResponse{}, requestFailure(ErrConflict, "saved view name already exists")
		}
		return UpdateTaskBoardSavedViewResponse{}, fmt.Errorf("update task board saved view: %w", err)
	}
	if tag.RowsAffected() == 0 {
		return UpdateTaskBoardSavedViewResponse{}, ErrNotFound
	}
	savedView, err := loadTaskBoardSavedView(ctx, txs.tx, workspaceID, memberID, savedViewID)
	if err != nil {
		return UpdateTaskBoardSavedViewResponse{}, err
	}
	return UpdateTaskBoardSavedViewResponse{SavedView: savedView}, nil
}

func (txs TxStore) DeleteTaskBoardSavedView(ctx context.Context, request DeleteTaskBoardSavedViewRequest) (DeleteTaskBoardSavedViewResponse, error) {
	workspaceID, memberID, err := txs.resolveTaskBoardSavedViewMember(ctx, request.Context)
	if err != nil {
		return DeleteTaskBoardSavedViewResponse{}, err
	}
	savedViewID, err := requireTaskBoardSavedViewID(request.SavedViewID)
	if err != nil {
		return DeleteTaskBoardSavedViewResponse{}, err
	}
	tag, err := txs.tx.Exec(ctx, `
DELETE FROM task_board_saved_views
WHERE workspace_id = $1
  AND member_id = $2
  AND id = $3
`, workspaceID, memberID, savedViewID)
	if err != nil {
		return DeleteTaskBoardSavedViewResponse{}, fmt.Errorf("delete task board saved view: %w", err)
	}
	if tag.RowsAffected() == 0 {
		return DeleteTaskBoardSavedViewResponse{}, ErrNotFound
	}
	return DeleteTaskBoardSavedViewResponse{}, nil
}

func (txs TxStore) resolveTaskBoardSavedViewMember(ctx context.Context, serviceContext ServiceContext) (string, string, error) {
	workspaceID, err := serviceContext.requireWorkspaceID()
	if err != nil {
		return "", "", err
	}
	userID, _, err := serviceContext.requireUser()
	if err != nil {
		return "", "", err
	}
	membership, err := access.LoadHumanWorkspaceMember(ctx, txs.db, userID, workspaceID)
	if errors.Is(err, access.ErrForbidden) {
		return "", "", ErrForbidden
	}
	if err != nil {
		return "", "", fmt.Errorf("load task board saved view workspace member: %w", err)
	}
	return workspaceID, membership.Member.ID, nil
}

func requireTaskBoardSavedViewID(value string) (string, error) {
	trimmed := strings.TrimSpace(value)
	if trimmed == "" {
		return "", badRequest("savedViewId is required")
	}
	return trimmed, nil
}

func normalizeTaskBoardSavedViewName(value string) (string, error) {
	trimmed := strings.Join(strings.Fields(value), " ")
	if trimmed == "" {
		return "", badRequest("saved view name is required")
	}
	runes := []rune(trimmed)
	if len(runes) > taskBoardSavedViewNameMaxRunes {
		return "", badRequest("saved view name is too long")
	}
	return trimmed, nil
}

func loadTaskBoardSavedViews(ctx context.Context, queryer coredb.Queryer, workspaceID string, memberID string) ([]TaskBoardSavedView, error) {
	rows, err := queryer.Query(ctx, `
SELECT id, workspace_id, member_id, name, state_json, created_at, updated_at
FROM task_board_saved_views
WHERE workspace_id = $1
  AND member_id = $2
ORDER BY updated_at DESC, created_at DESC, id ASC
`, strings.TrimSpace(workspaceID), strings.TrimSpace(memberID))
	if err != nil {
		return nil, fmt.Errorf("list task board saved views: %w", err)
	}
	defer rows.Close()
	return scanTaskBoardSavedViews(rows)
}

func loadTaskBoardSavedView(ctx context.Context, queryer coredb.QueryRower, workspaceID string, memberID string, savedViewID string) (TaskBoardSavedView, error) {
	row := queryer.QueryRow(ctx, `
SELECT id, workspace_id, member_id, name, state_json, created_at, updated_at
FROM task_board_saved_views
WHERE workspace_id = $1
  AND member_id = $2
  AND id = $3
`, strings.TrimSpace(workspaceID), strings.TrimSpace(memberID), strings.TrimSpace(savedViewID))

	savedView, err := scanTaskBoardSavedView(row)
	if err != nil {
		if coredb.IsNotFound(err) {
			return TaskBoardSavedView{}, ErrNotFound
		}
		return TaskBoardSavedView{}, fmt.Errorf("load task board saved view: %w", err)
	}
	return savedView, nil
}

type taskBoardSavedViewScanner interface {
	Scan(dest ...any) error
}

func scanTaskBoardSavedViews(rows coredb.Rows) ([]TaskBoardSavedView, error) {
	savedViews := make([]TaskBoardSavedView, 0)
	for rows.Next() {
		savedView, err := scanTaskBoardSavedView(rows)
		if err != nil {
			return nil, err
		}
		savedViews = append(savedViews, savedView)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate task board saved views: %w", err)
	}
	return savedViews, nil
}

func scanTaskBoardSavedView(scanner taskBoardSavedViewScanner) (TaskBoardSavedView, error) {
	var savedView TaskBoardSavedView
	var raw string
	if err := scanner.Scan(
		&savedView.ID,
		&savedView.WorkspaceID,
		&savedView.MemberID,
		&savedView.Name,
		&raw,
		&savedView.CreatedAt,
		&savedView.UpdatedAt,
	); err != nil {
		return TaskBoardSavedView{}, err
	}
	decoded, err := unmarshalTaskBoardViewStateJSON([]byte(raw))
	if err != nil {
		return TaskBoardSavedView{}, fmt.Errorf("decode task board saved view state: %w", err)
	}
	savedView.ViewState = normalizeTaskBoardViewState(decoded)
	return savedView, nil
}
