package app

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	"sub2api-cn-relay-manager/internal/store/sqlite"
)

type PortalLogicalGroupInfo struct {
	LogicalGroupID   string                    `json:"logical_group_id"`
	DisplayName      string                    `json:"display_name"`
	Description      string                    `json:"description,omitempty"`
	Status           string                    `json:"status"`
	StickyMode       string                    `json:"sticky_mode,omitempty"`
	RoutePolicy      string                    `json:"route_policy,omitempty"`
	PublicModels     []PortalLogicalGroupModel `json:"public_models,omitempty"`
	RouteCount       int                       `json:"route_count"`
	ActiveRouteCount int                       `json:"active_route_count"`
	CreatedAt        string                    `json:"created_at,omitempty"`
	UpdatedAt        string                    `json:"updated_at,omitempty"`
}

type PortalLogicalGroupModel struct {
	PublicModel string `json:"public_model"`
	Status      string `json:"status,omitempty"`
}

func handleListPortalLogicalGroups(w http.ResponseWriter, r *http.Request, fn func(context.Context) ([]PortalLogicalGroupInfo, error)) {
	if fn == nil {
		writeHTTPError(w, &httpError{StatusCode: http.StatusInternalServerError, Code: "server_misconfigured", Message: "list-portal-logical-groups action is not configured"})
		return
	}
	groups, err := fn(r.Context())
	if err != nil {
		writeHTTPError(w, classifyError(err))
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"logical_groups": groups})
}

func handleGetPortalLogicalGroup(w http.ResponseWriter, r *http.Request, fn func(context.Context, string) (PortalLogicalGroupInfo, error)) {
	if fn == nil {
		writeHTTPError(w, &httpError{StatusCode: http.StatusInternalServerError, Code: "server_misconfigured", Message: "get-portal-logical-group action is not configured"})
		return
	}
	group, err := fn(r.Context(), strings.TrimSpace(r.PathValue("groupID")))
	if err != nil {
		writeHTTPError(w, classifyError(err))
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"logical_group": group})
}

func handleListPortalLogicalGroupModels(w http.ResponseWriter, r *http.Request, fn func(context.Context, string) ([]PortalLogicalGroupModel, error)) {
	if fn == nil {
		writeHTTPError(w, &httpError{StatusCode: http.StatusInternalServerError, Code: "server_misconfigured", Message: "list-portal-logical-group-models action is not configured"})
		return
	}
	models, err := fn(r.Context(), strings.TrimSpace(r.PathValue("groupID")))
	if err != nil {
		writeHTTPError(w, classifyError(err))
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"models": models})
}

func buildListPortalLogicalGroupsAction(sqliteDSN string) func(context.Context) ([]PortalLogicalGroupInfo, error) {
	return func(ctx context.Context) ([]PortalLogicalGroupInfo, error) {
		store, err := sqlite.Open(ctx, sqliteDSN)
		if err != nil {
			return nil, err
		}
		defer store.Close()

		rows, err := store.LogicalGroups().List(ctx)
		if err != nil {
			return nil, err
		}
		items := make([]PortalLogicalGroupInfo, 0, len(rows))
		for _, row := range rows {
			if !isPortalLogicalGroupVisible(row.Status) {
				continue
			}
			item, buildErr := buildPortalLogicalGroupInfo(ctx, store, row)
			if buildErr != nil {
				return nil, buildErr
			}
			items = append(items, item)
		}
		return items, nil
	}
}

func buildGetPortalLogicalGroupAction(sqliteDSN string) func(context.Context, string) (PortalLogicalGroupInfo, error) {
	return func(ctx context.Context, logicalGroupID string) (PortalLogicalGroupInfo, error) {
		store, err := sqlite.Open(ctx, sqliteDSN)
		if err != nil {
			return PortalLogicalGroupInfo{}, err
		}
		defer store.Close()

		group, err := getLogicalGroupRow(ctx, store, logicalGroupID)
		if err != nil {
			return PortalLogicalGroupInfo{}, err
		}
		if !isPortalLogicalGroupVisible(group.Status) {
			return PortalLogicalGroupInfo{}, fmt.Errorf("logical group %q not found", strings.TrimSpace(logicalGroupID))
		}
		return buildPortalLogicalGroupInfo(ctx, store, group)
	}
}

func buildListPortalLogicalGroupModelsAction(sqliteDSN string) func(context.Context, string) ([]PortalLogicalGroupModel, error) {
	return func(ctx context.Context, logicalGroupID string) ([]PortalLogicalGroupModel, error) {
		store, err := sqlite.Open(ctx, sqliteDSN)
		if err != nil {
			return nil, err
		}
		defer store.Close()

		group, err := getLogicalGroupRow(ctx, store, logicalGroupID)
		if err != nil {
			return nil, err
		}
		if !isPortalLogicalGroupVisible(group.Status) {
			return nil, fmt.Errorf("logical group %q not found", strings.TrimSpace(logicalGroupID))
		}
		rows, err := store.LogicalGroupModels().ListByLogicalGroupID(ctx, group.LogicalGroupID)
		if err != nil {
			return nil, err
		}
		return portalLogicalGroupModelsFromRows(rows), nil
	}
}

func buildPortalLogicalGroupInfo(ctx context.Context, store *sqlite.DB, group sqlite.LogicalGroup) (PortalLogicalGroupInfo, error) {
	modelRows, err := store.LogicalGroupModels().ListByLogicalGroupID(ctx, group.LogicalGroupID)
	if err != nil {
		return PortalLogicalGroupInfo{}, err
	}
	routeRows, err := store.LogicalGroupRoutes().ListByLogicalGroupID(ctx, group.LogicalGroupID)
	if err != nil {
		return PortalLogicalGroupInfo{}, err
	}

	routeCount := 0
	activeRouteCount := 0
	for _, route := range routeRows {
		routeCount++
		if strings.EqualFold(strings.TrimSpace(route.Status), "active") {
			activeRouteCount++
		}
	}

	return PortalLogicalGroupInfo{
		LogicalGroupID:   group.LogicalGroupID,
		DisplayName:      group.DisplayName,
		Description:      group.Description,
		Status:           group.Status,
		StickyMode:       group.StickyMode,
		RoutePolicy:      group.RoutePolicy,
		PublicModels:     portalLogicalGroupModelsFromRows(modelRows),
		RouteCount:       routeCount,
		ActiveRouteCount: activeRouteCount,
		CreatedAt:        group.CreatedAt,
		UpdatedAt:        group.UpdatedAt,
	}, nil
}

func portalLogicalGroupModelsFromRows(rows []sqlite.LogicalGroupModel) []PortalLogicalGroupModel {
	result := make([]PortalLogicalGroupModel, 0, len(rows))
	for _, row := range rows {
		if !isPortalLogicalGroupModelVisible(row.Status) {
			continue
		}
		result = append(result, PortalLogicalGroupModel{
			PublicModel: row.PublicModel,
			Status:      row.Status,
		})
	}
	return result
}

func isPortalLogicalGroupVisible(status string) bool {
	return strings.EqualFold(strings.TrimSpace(status), "active")
}

func isPortalLogicalGroupModelVisible(status string) bool {
	status = strings.TrimSpace(status)
	return status == "" || strings.EqualFold(status, "active")
}
