package org

import (
	"net/http"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/api/dto/converter"
	"github.com/tidbcloud/AgentTeam/internal/store/organization"

	"github.com/gin-gonic/gin"
)

// List handles GET /api/v1/orgs/:orgId/providers.
// It returns all org-managed providers for the specified organization.
func (handler *ProviderHandler) List(ctx *gin.Context) {
	authenticated, ok := api.AuthenticatedUserFromContext(ctx)
	if !ok {
		api.Error(ctx, http.StatusUnauthorized, "Unauthorized")
		return
	}
	orgs, ok := handler.organizationProvider()
	if !ok {
		api.Error(ctx, http.StatusNotFound, "Organization provider not installed")
		return
	}

	organizationID := strings.TrimSpace(ctx.Param("orgId"))
	if organizationID == "" {
		api.Error(ctx, http.StatusBadRequest, "orgId is required")
		return
	}

	providers, err := orgs.ListOrganizationProvidersForUser(ctx.Request.Context(), authenticated.User.ID, organizationID)
	if err != nil {
		api.StoreError(ctx, err)
		return
	}
	ctx.JSON(http.StatusOK, converter.ToOrgProviderViews(providers))
}

// Create handles POST /api/v1/orgs/:orgId/providers.
// It creates an org-managed provider record.
func (handler *ProviderHandler) Create(ctx *gin.Context) {
	authenticated, ok := api.AuthenticatedUserFromContext(ctx)
	if !ok {
		api.Error(ctx, http.StatusUnauthorized, "Unauthorized")
		return
	}
	orgs, ok := handler.organizationProvider()
	if !ok {
		api.Error(ctx, http.StatusNotFound, "Organization provider not installed")
		return
	}

	organizationID := strings.TrimSpace(ctx.Param("orgId"))
	if organizationID == "" {
		api.Error(ctx, http.StatusBadRequest, "orgId is required")
		return
	}

	var request dto.OrganizationProviderCreateRequest
	if err := ctx.ShouldBindJSON(&request); err != nil {
		api.Error(ctx, http.StatusBadRequest, "Invalid request body")
		return
	}

	provider, err := orgs.CreateOrganizationProviderForUser(ctx.Request.Context(), authenticated.User.ID, organizationID, organization.CreateOrganizationProviderParams{
		Name:         request.Name,
		Description:  request.Description,
		DriverKind:   request.DriverKind,
		BaseURL:      request.BaseURL,
		Endpoints:    providerEndpointsFromDTO(request.Endpoints),
		Capabilities: providerCapabilitiesFromDTO(request.Capabilities, request.ProviderType),
		Models:       providerModelsFromDTO(request.Models),
	})
	if err != nil {
		api.StoreError(ctx, err)
		return
	}
	if err := handler.refreshLLMRegistry(ctx.Request.Context()); err != nil {
		api.StoreError(ctx, err)
		return
	}
	ctx.JSON(http.StatusOK, converter.ToOrgProviderView(provider))
}

// Get handles GET /api/v1/orgs/:orgId/providers/:providerId.
// It returns one org-managed provider by ID.
func (handler *ProviderHandler) Get(ctx *gin.Context) {
	authenticated, ok := api.AuthenticatedUserFromContext(ctx)
	if !ok {
		api.Error(ctx, http.StatusUnauthorized, "Unauthorized")
		return
	}
	orgs, ok := handler.organizationProvider()
	if !ok {
		api.Error(ctx, http.StatusNotFound, "Organization provider not installed")
		return
	}

	organizationID := strings.TrimSpace(ctx.Param("orgId"))
	providerID := strings.TrimSpace(ctx.Param("providerId"))
	if organizationID == "" || providerID == "" {
		api.Error(ctx, http.StatusBadRequest, "Invalid request")
		return
	}

	provider, err := orgs.GetOrganizationProviderForUser(ctx.Request.Context(), authenticated.User.ID, organizationID, providerID)
	if err != nil {
		api.StoreError(ctx, err)
		return
	}
	if err := handler.refreshLLMRegistry(ctx.Request.Context()); err != nil {
		api.StoreError(ctx, err)
		return
	}
	ctx.JSON(http.StatusOK, converter.ToOrgProviderView(provider))
}

// Update handles PATCH /api/v1/orgs/:orgId/providers/:providerId.
// It patches the specified org-managed provider.
func (handler *ProviderHandler) Update(ctx *gin.Context) {
	authenticated, ok := api.AuthenticatedUserFromContext(ctx)
	if !ok {
		api.Error(ctx, http.StatusUnauthorized, "Unauthorized")
		return
	}
	orgs, ok := handler.organizationProvider()
	if !ok {
		api.Error(ctx, http.StatusNotFound, "Organization provider not installed")
		return
	}

	organizationID := strings.TrimSpace(ctx.Param("orgId"))
	providerID := strings.TrimSpace(ctx.Param("providerId"))
	if organizationID == "" || providerID == "" {
		api.Error(ctx, http.StatusBadRequest, "Invalid request")
		return
	}

	var request dto.OrganizationProviderUpdateRequest
	if err := ctx.ShouldBindJSON(&request); err != nil {
		api.Error(ctx, http.StatusBadRequest, "Invalid request body")
		return
	}

	provider, err := orgs.UpdateOrganizationProviderForUser(ctx.Request.Context(), authenticated.User.ID, organizationID, providerID, organization.UpdateOrganizationProviderParams{
		Name:         request.Name,
		Description:  request.Description,
		DriverKind:   request.DriverKind,
		BaseURL:      request.BaseURL,
		Endpoints:    providerEndpointsPtrFromDTO(request.Endpoints),
		Capabilities: providerCapabilitiesPtrFromDTO(request.Capabilities, request.ProviderType),
		Models:       providerModelsPtrFromDTO(request.Models),
		Status:       request.Status,
	})
	if err != nil {
		api.StoreError(ctx, err)
		return
	}
	if err := handler.refreshLLMRegistry(ctx.Request.Context()); err != nil {
		api.StoreError(ctx, err)
		return
	}
	ctx.JSON(http.StatusOK, converter.ToOrgProviderView(provider))
}

// Delete handles DELETE /api/v1/orgs/:orgId/providers/:providerId.
// It deletes the specified org-managed provider.
func (handler *ProviderHandler) Delete(ctx *gin.Context) {
	authenticated, ok := api.AuthenticatedUserFromContext(ctx)
	if !ok {
		api.Error(ctx, http.StatusUnauthorized, "Unauthorized")
		return
	}
	orgs, ok := handler.organizationProvider()
	if !ok {
		api.Error(ctx, http.StatusNotFound, "Organization provider not installed")
		return
	}

	organizationID := strings.TrimSpace(ctx.Param("orgId"))
	providerID := strings.TrimSpace(ctx.Param("providerId"))
	if organizationID == "" || providerID == "" {
		api.Error(ctx, http.StatusBadRequest, "Invalid request")
		return
	}

	if err := orgs.DeleteOrganizationProviderForUser(ctx.Request.Context(), authenticated.User.ID, organizationID, providerID); err != nil {
		api.StoreError(ctx, err)
		return
	}
	if err := handler.refreshLLMRegistry(ctx.Request.Context()); err != nil {
		api.StoreError(ctx, err)
		return
	}
	ctx.JSON(http.StatusOK, dto.EmptyResponse{})
}

// WorkspaceGrantsList handles GET /api/v1/orgs/:orgId/workspaces/:workspaceId/providers.
// It lists provider grants for the specified organization workspace.
func (handler *ProviderHandler) WorkspaceGrantsList(ctx *gin.Context) {
	authenticated, ok := api.AuthenticatedUserFromContext(ctx)
	if !ok {
		api.Error(ctx, http.StatusUnauthorized, "Unauthorized")
		return
	}
	orgs, ok := handler.organizationProvider()
	if !ok {
		api.Error(ctx, http.StatusNotFound, "Organization provider not installed")
		return
	}

	organizationID := strings.TrimSpace(ctx.Param("orgId"))
	workspaceID := strings.TrimSpace(ctx.Param("workspaceId"))
	if organizationID == "" || workspaceID == "" {
		api.Error(ctx, http.StatusBadRequest, "Invalid request")
		return
	}

	grants, err := orgs.ListOrganizationWorkspaceProviderGrantsForUser(ctx.Request.Context(), authenticated.User.ID, organizationID, workspaceID)
	if err != nil {
		api.StoreError(ctx, err)
		return
	}
	ctx.JSON(http.StatusOK, converter.ToOrgWorkspaceProviderGrantViews(grants))
}

// WorkspaceGrantCreate handles POST /api/v1/orgs/:orgId/workspaces/:workspaceId/providers/:providerId.
// It grants an org-managed provider to the specified workspace.
func (handler *ProviderHandler) WorkspaceGrantCreate(ctx *gin.Context) {
	authenticated, ok := api.AuthenticatedUserFromContext(ctx)
	if !ok {
		api.Error(ctx, http.StatusUnauthorized, "Unauthorized")
		return
	}
	orgs, ok := handler.organizationProvider()
	if !ok {
		api.Error(ctx, http.StatusNotFound, "Organization provider not installed")
		return
	}

	organizationID := strings.TrimSpace(ctx.Param("orgId"))
	workspaceID := strings.TrimSpace(ctx.Param("workspaceId"))
	providerID := strings.TrimSpace(ctx.Param("providerId"))
	if organizationID == "" || workspaceID == "" || providerID == "" {
		api.Error(ctx, http.StatusBadRequest, "Invalid request")
		return
	}

	grant, err := orgs.GrantOrganizationWorkspaceProviderForUser(ctx.Request.Context(), authenticated.User.ID, organizationID, workspaceID, providerID)
	if err != nil {
		api.StoreError(ctx, err)
		return
	}
	if err := handler.refreshLLMRegistry(ctx.Request.Context()); err != nil {
		api.StoreError(ctx, err)
		return
	}
	ctx.JSON(http.StatusOK, converter.ToOrgWorkspaceProviderGrantView(grant))
}

// WorkspaceGrantDelete handles DELETE /api/v1/orgs/:orgId/workspaces/:workspaceId/providers/:providerId.
// It revokes an org-managed provider grant from the specified workspace.
func (handler *ProviderHandler) WorkspaceGrantDelete(ctx *gin.Context) {
	authenticated, ok := api.AuthenticatedUserFromContext(ctx)
	if !ok {
		api.Error(ctx, http.StatusUnauthorized, "Unauthorized")
		return
	}
	orgs, ok := handler.organizationProvider()
	if !ok {
		api.Error(ctx, http.StatusNotFound, "Organization provider not installed")
		return
	}

	organizationID := strings.TrimSpace(ctx.Param("orgId"))
	workspaceID := strings.TrimSpace(ctx.Param("workspaceId"))
	providerID := strings.TrimSpace(ctx.Param("providerId"))
	if organizationID == "" || workspaceID == "" || providerID == "" {
		api.Error(ctx, http.StatusBadRequest, "Invalid request")
		return
	}

	if err := orgs.RevokeOrganizationWorkspaceProviderForUser(ctx.Request.Context(), authenticated.User.ID, organizationID, workspaceID, providerID); err != nil {
		api.StoreError(ctx, err)
		return
	}
	if err := handler.refreshLLMRegistry(ctx.Request.Context()); err != nil {
		api.StoreError(ctx, err)
		return
	}
	ctx.JSON(http.StatusOK, dto.EmptyResponse{})
}

func providerModelsFromDTO(items []dto.OrganizationProviderModelInput) []organization.OrganizationProviderModel {
	models := make([]organization.OrganizationProviderModel, 0, len(items))
	for _, item := range items {
		models = append(models, organization.OrganizationProviderModel{
			Name: item.Name,
			ID:   item.ID,
		})
	}
	return models
}

func providerModelsPtrFromDTO(items *[]dto.OrganizationProviderModelInput) *[]organization.OrganizationProviderModel {
	if items == nil {
		return nil
	}
	models := providerModelsFromDTO(*items)
	return &models
}

func providerEndpointsFromDTO(input dto.OrganizationProviderEndpointsInput) organization.OrganizationProviderEndpoints {
	return organization.OrganizationProviderEndpoints{
		MessagesBaseURL:   input.MessagesBaseURL,
		ResponsesBaseURL:  input.ResponsesBaseURL,
		EmbeddingsBaseURL: input.EmbeddingsBaseURL,
	}
}

func providerEndpointsPtrFromDTO(input *dto.OrganizationProviderEndpointsInput) *organization.OrganizationProviderEndpoints {
	if input == nil {
		return nil
	}
	endpoints := providerEndpointsFromDTO(*input)
	return &endpoints
}

func providerCapabilitiesFromDTO(input *dto.OrganizationProviderCapabilitiesInput, deprecatedProviderType string) organization.OrganizationProviderCapabilities {
	if input != nil {
		capabilities := organization.OrganizationProviderCapabilities{
			OpenAICompatible:    input.OpenAICompatible,
			AnthropicCompatible: input.AnthropicCompatible,
			ResponsesCompatible: input.ResponsesCompatible,
			EmbeddingCompatible: input.EmbeddingCompatible,
		}
		if capabilities != (organization.OrganizationProviderCapabilities{}) {
			return capabilities
		}
	}
	var capabilities organization.OrganizationProviderCapabilities
	switch providerType := strings.TrimSpace(deprecatedProviderType); providerType {
	case "anthropic":
		capabilities.AnthropicCompatible = true
	case "responses":
		capabilities.ResponsesCompatible = true
	case "":
	default:
		capabilities.OpenAICompatible = true
	}
	return capabilities
}

func providerCapabilitiesPtrFromDTO(input *dto.OrganizationProviderCapabilitiesInput, deprecatedProviderType *string) *organization.OrganizationProviderCapabilities {
	if input == nil && (deprecatedProviderType == nil || strings.TrimSpace(*deprecatedProviderType) == "") {
		return nil
	}
	var payload dto.OrganizationProviderCapabilitiesInput
	if input != nil {
		payload = *input
	}
	deprecated := ""
	if deprecatedProviderType != nil {
		deprecated = *deprecatedProviderType
	}
	capabilities := providerCapabilitiesFromDTO(&payload, deprecated)
	return &capabilities
}
