package taskboard

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

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

const (
	taskBoardDefaultView     = "board"
	taskBoardDefaultSortMode = "manual"
)

var (
	allowedTaskBoardViews = map[string]struct{}{
		"board": {},
		"list":  {},
	}
	allowedTaskBoardQuickFilters = map[string]struct{}{
		"related_to_me":       {},
		"assigned_to_agents":  {},
		"blocked":             {},
		"needs_clarification": {},
		"created_by_me":       {},
	}
	allowedTaskBoardStructuredFilterKeys = map[string]struct{}{
		"status":   {},
		"channel":  {},
		"assignee": {},
		"kind":     {},
		"priority": {},
	}
	allowedTaskBoardSortModes = map[string]struct{}{
		"manual":           {},
		"priority":         {},
		"recently_updated": {},
		"created_time":     {},
		"title":            {},
	}
	allowedTaskBoardCardProperties = map[string]struct{}{
		"description":         {},
		"status":              {},
		"assignee":            {},
		"comment_count":       {},
		"priority":            {},
		"kind":                {},
		"subtask_progress":    {},
		"checkpoint_progress": {},
		"last_activity":       {},
	}
)

func (txs TxStore) GetTaskBoardViewState(ctx context.Context, request GetTaskBoardViewStateRequest) (GetTaskBoardViewStateResponse, error) {
	workspaceID, err := request.Context.requireWorkspaceID()
	if err != nil {
		return GetTaskBoardViewStateResponse{}, err
	}
	userID, _, err := request.Context.requireUser()
	if err != nil {
		return GetTaskBoardViewStateResponse{}, err
	}
	channelID, err := request.Board.requireChannelID()
	if err != nil {
		return GetTaskBoardViewStateResponse{}, err
	}

	channelAccess, err := txs.loadAuthorizedUserTaskChannelAccess(ctx, txs.db, userID, workspaceID, channelID, taskReadChannelAction)
	if err != nil {
		return GetTaskBoardViewStateResponse{}, err
	}

	viewState, err := loadTaskBoardViewState(ctx, txs.db, workspaceID, channelID, channelAccess.WorkspaceMember.Member.ID)
	if err != nil {
		return GetTaskBoardViewStateResponse{}, err
	}
	return GetTaskBoardViewStateResponse{ViewState: viewState}, nil
}

func (txs TxStore) PutTaskBoardViewState(ctx context.Context, request PutTaskBoardViewStateRequest) (PutTaskBoardViewStateResponse, error) {
	workspaceID, err := request.Context.requireWorkspaceID()
	if err != nil {
		return PutTaskBoardViewStateResponse{}, err
	}
	userID, _, err := request.Context.requireUser()
	if err != nil {
		return PutTaskBoardViewStateResponse{}, err
	}
	channelID, err := request.Board.requireChannelID()
	if err != nil {
		return PutTaskBoardViewStateResponse{}, err
	}

	tx := txs.tx
	channelAccess, err := txs.loadAuthorizedUserTaskChannelAccess(ctx, tx, userID, workspaceID, channelID, taskReadChannelAction)
	if err != nil {
		return PutTaskBoardViewStateResponse{}, err
	}
	normalized := normalizeTaskBoardViewState(request.ViewState)
	if request.ViewState.CardPropertyToggles == nil {
		existing, loadErr := loadTaskBoardViewState(
			ctx,
			tx,
			workspaceID,
			channelID,
			channelAccess.WorkspaceMember.Member.ID,
		)
		if loadErr != nil {
			return PutTaskBoardViewStateResponse{}, loadErr
		}
		normalized = preserveTaskBoardCardPropertyToggles(normalized, existing)
	}
	if err := writeTaskBoardViewState(ctx, tx, workspaceID, channelID, channelAccess.WorkspaceMember.Member.ID, normalized); err != nil {
		return PutTaskBoardViewStateResponse{}, err
	}
	return PutTaskBoardViewStateResponse{ViewState: normalized}, nil
}

func defaultTaskBoardViewState() TaskBoardViewState {
	return TaskBoardViewState{
		View:                taskBoardDefaultView,
		SearchQuery:         "",
		QuickFilters:        []string{},
		StructuredFilters:   map[string][]string{},
		SortMode:            taskBoardDefaultSortMode,
		CardPropertyToggles: []string{},
		DoneCollapsed:       false,
	}
}

func normalizeTaskBoardViewState(input TaskBoardViewState) TaskBoardViewState {
	return TaskBoardViewState{
		View:                normalizeTaskBoardView(input.View),
		SearchQuery:         normalizeTaskBoardSearchQuery(input.SearchQuery),
		QuickFilters:        normalizeTaskBoardViewStateList(input.QuickFilters, allowedTaskBoardQuickFilters),
		StructuredFilters:   normalizeTaskBoardStructuredFilters(input.StructuredFilters),
		SortMode:            normalizeTaskBoardSortMode(input.SortMode),
		CardPropertyToggles: normalizeTaskBoardCardPropertyToggles(input.CardPropertyToggles),
		DoneCollapsed:       input.DoneCollapsed,
	}
}

func normalizeTaskBoardCardPropertyToggles(values []string) []string {
	if values == nil {
		return nil
	}
	return normalizeTaskBoardViewStateList(values, allowedTaskBoardCardProperties)
}

func preserveTaskBoardCardPropertyToggles(
	next TaskBoardViewState,
	existing TaskBoardViewState,
) TaskBoardViewState {
	next.CardPropertyToggles = append(
		[]string(nil),
		existing.CardPropertyToggles...,
	)
	return next
}

func normalizeTaskBoardView(value string) string {
	normalized := strings.ToLower(strings.TrimSpace(value))
	if _, ok := allowedTaskBoardViews[normalized]; ok {
		return normalized
	}
	return taskBoardDefaultView
}

func normalizeTaskBoardSortMode(value string) string {
	normalized := strings.ToLower(strings.TrimSpace(value))
	if _, ok := allowedTaskBoardSortModes[normalized]; ok {
		return normalized
	}
	return taskBoardDefaultSortMode
}

func normalizeTaskBoardSearchQuery(value string) string {
	return strings.Join(strings.Fields(value), " ")
}

func normalizeTaskBoardViewStateList(values []string, allowed map[string]struct{}) []string {
	normalized := make([]string, 0, len(values))
	seen := make(map[string]struct{}, len(values))
	for _, value := range values {
		trimmed := strings.TrimSpace(value)
		if trimmed == "" {
			continue
		}
		if allowed != nil {
			if _, ok := allowed[trimmed]; !ok {
				continue
			}
		}
		if _, exists := seen[trimmed]; exists {
			continue
		}
		seen[trimmed] = struct{}{}
		normalized = append(normalized, trimmed)
	}
	return normalized
}

func normalizeTaskBoardStructuredFilters(filters map[string][]string) map[string][]string {
	normalized := make(map[string][]string, len(filters))
	for key, values := range filters {
		trimmedKey := strings.TrimSpace(key)
		if trimmedKey == "" {
			continue
		}
		if _, ok := allowedTaskBoardStructuredFilterKeys[trimmedKey]; !ok {
			continue
		}
		normalizedValues := normalizeTaskBoardViewStateList(values, nil)
		if len(normalizedValues) == 0 {
			continue
		}
		normalized[trimmedKey] = normalizedValues
	}
	return normalized
}

func loadTaskBoardViewState(ctx context.Context, queryer coredb.QueryRower, workspaceID string, channelID string, memberID string) (TaskBoardViewState, error) {
	row := queryer.QueryRow(ctx, `
SELECT state_json
FROM task_board_view_states
WHERE workspace_id = $1
  AND channel_id = $2
  AND member_id = $3
`, strings.TrimSpace(workspaceID), strings.TrimSpace(channelID), strings.TrimSpace(memberID))

	var raw string
	if err := row.Scan(&raw); err != nil {
		if coredb.IsNotFound(err) {
			return defaultTaskBoardViewState(), nil
		}
		return TaskBoardViewState{}, fmt.Errorf("load task board view state: %w", err)
	}
	if strings.TrimSpace(raw) == "" {
		return defaultTaskBoardViewState(), nil
	}

	decoded, err := unmarshalTaskBoardViewStateJSON([]byte(raw))
	if err != nil {
		return TaskBoardViewState{}, fmt.Errorf("decode task board view state: %w", err)
	}
	return normalizeTaskBoardViewState(decoded), nil
}

func writeTaskBoardViewState(ctx context.Context, execer queryExecer, workspaceID string, channelID string, memberID string, viewState TaskBoardViewState) error {
	payload, err := marshalTaskBoardViewStateJSON(viewState)
	if err != nil {
		return fmt.Errorf("marshal task board view state: %w", err)
	}

	now := time.Now().UTC()
	updateTag, err := execer.Exec(ctx, `
UPDATE task_board_view_states
SET state_json = $4,
    updated_at = $5
WHERE workspace_id = $1
  AND channel_id = $2
  AND member_id = $3
`, strings.TrimSpace(workspaceID), strings.TrimSpace(channelID), strings.TrimSpace(memberID), string(payload), now)
	if err != nil {
		return fmt.Errorf("update task board view state: %w", err)
	}
	if updateTag.RowsAffected() > 0 {
		return nil
	}

	if _, err := execer.Exec(ctx, `
INSERT INTO task_board_view_states (
    workspace_id,
    channel_id,
    member_id,
    state_json,
    created_at,
    updated_at
) VALUES ($1, $2, $3, $4, $5, $5)
`, strings.TrimSpace(workspaceID), strings.TrimSpace(channelID), strings.TrimSpace(memberID), string(payload), now); err != nil {
		if coredb.IsUniqueViolation(err) {
			retryTag, retryErr := execer.Exec(ctx, `
UPDATE task_board_view_states
SET state_json = $4,
    updated_at = $5
WHERE workspace_id = $1
  AND channel_id = $2
  AND member_id = $3
`, strings.TrimSpace(workspaceID), strings.TrimSpace(channelID), strings.TrimSpace(memberID), string(payload), now)
			if retryErr != nil {
				return fmt.Errorf("retry update task board view state: %w", retryErr)
			}
			if retryTag.RowsAffected() > 0 {
				return nil
			}
		}
		return fmt.Errorf("insert task board view state: %w", err)
	}
	return nil
}

func marshalTaskBoardViewStateJSON(viewState TaskBoardViewState) ([]byte, error) {
	return model.MarshalTaskBoardViewState(model.TaskBoardViewStateJSON{
		View:                viewState.View,
		SearchQuery:         viewState.SearchQuery,
		QuickFilters:        viewState.QuickFilters,
		StructuredFilters:   viewState.StructuredFilters,
		SortMode:            viewState.SortMode,
		CardPropertyToggles: viewState.CardPropertyToggles,
		DoneCollapsed:       viewState.DoneCollapsed,
	})
}

func unmarshalTaskBoardViewStateJSON(raw []byte) (TaskBoardViewState, error) {
	decoded, err := model.UnmarshalTaskBoardViewState(raw)
	if err != nil {
		return TaskBoardViewState{}, err
	}
	return TaskBoardViewState{
		View:                decoded.View,
		SearchQuery:         decoded.SearchQuery,
		QuickFilters:        decoded.QuickFilters,
		StructuredFilters:   decoded.StructuredFilters,
		SortMode:            decoded.SortMode,
		CardPropertyToggles: decoded.CardPropertyToggles,
		DoneCollapsed:       decoded.DoneCollapsed,
	}, nil
}
