package organization

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

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

const (
	organizationRoleOwner  = "org_owner"
	organizationRoleAdmin  = "org_admin"
	organizationRoleMember = "org_member"

	organizationAgentTeamProxyProviderRef = "agentteam_proxy"

	organizationDirectoryBackendEmbedded          = "embedded"
	organizationDirectoryBackendExternalShadow    = "external_shadow"
	organizationDirectoryBackendExternalAuthority = "external_authority"
	organizationProviderFeishu                    = "feishu"

	organizationSyncModeLocal     = "local"
	organizationSyncModeShadow    = "shadow"
	organizationSyncModeAuthority = "authority"
	organizationSyncSourceDefault = "directory"

	organizationTeamStatusActive = "active"
	organizationTeamSourceManual = "manual"
	organizationTeamSourceSynced = "synced"

	organizationMemberStatusActive          = "active"
	organizationMemberSourceLocal           = "local"
	organizationMemberSourceInvite          = "invite"
	organizationMemberSourceSync            = "sync"
	organizationMemberSourceSSO             = "sso"
	organizationProviderStatusActive        = "active"
	organizationProviderStatusInactive      = "inactive"
	organizationProviderTokenStatusActive   = "active"
	organizationProviderTokenStatusDisabled = "disabled"
	organizationLLMProxyKeyStatusActive     = "active"
	organizationLLMProxyKeyStatusDisabled   = "disabled"

	organizationHostedPickerVisibilityShowDisabled  = "show_disabled_when_unavailable"
	organizationHostedPickerVisibilityHideDisabled  = "hide_disabled_when_unavailable"
	organizationHostedAuthorizationModeAllActive    = "all_active_hosted_inventory"
	organizationHostedAuthorizationModeSelected     = "selected_hosted_models"
	organizationHostedAuthorizationModeUnauthorized = "workspace_not_authorized"
	organizationHostedAuthorizationStatusActive     = "active"
	organizationHostedAuthorizationStatusInactive   = "inactive"
	organizationHostedAgentLLMMode                  = "agentteam_hosted"

	organizationAuthSubjectKeySubject = "subject"
	organizationAuthSubjectKeyOpenID  = "open_id"
	organizationAuthSubjectKeyUnionID = "union_id"
)

const feishuAutoRootDepartmentID = "0"

type CoreBridge interface {
	FindUserByEmail(ctx context.Context, email string) (model.User, error)
	FindUserByID(ctx context.Context, userID string) (model.User, error)
	EnsureOrganizationDirectoryUser(ctx context.Context, params EnsureOrganizationDirectoryUserParams) (model.User, bool, error)
	RequireWorkspaceMemberManagementForUser(ctx context.Context, userID string, workspaceID string) error
	RequireWorkspaceMemberRoleManagementForUser(ctx context.Context, userID string, workspaceID string) error
	RequireWorkspaceRuntimeManagementForUser(ctx context.Context, userID string, workspaceID string) error
	CreateDefaultWorkspaceWithNewOrganizationInTx(ctx context.Context, tx coredb.Tx, owner model.User, name string) (WorkspaceSummary, error)
	CreateWorkspaceInTx(ctx context.Context, tx coredb.Tx, organizationID string, owner model.User, name string) (WorkspaceSummary, error)
	CreateWorkspaceWithSlugInTx(ctx context.Context, tx coredb.Tx, organizationID string, owner model.User, name string, slug string) (WorkspaceSummary, error)
	EnsureHumanWorkspaceMemberInTx(ctx context.Context, tx coredb.Tx, workspaceID string, userID string, requestedRole string) (string, string, error)
	RestoreHumanWorkspaceMemberBaselineInTx(ctx context.Context, tx coredb.Tx, workspaceID string, userID string, baselineRole string) (string, error)
	DeactivateHumanWorkspaceMemberInTx(ctx context.Context, tx coredb.Tx, workspaceID string, userID string) (string, error)
	ArchiveWorkspaceMemberRuntimeResourcesInTx(ctx context.Context, tx coredb.Tx, workspaceID string, userID string) ([]string, error)
}

type ServiceOptions struct {
	FeishuBaseURL                 string
	FeishuHTTPClient              *http.Client
	FeishuAppID                   string
	FeishuAppSecret               string
	FeishuCredentialEncryptionKey []byte
	CheckOrganizationQuotaInTx    func(context.Context, coredb.Tx, string, plan.MetricKey, int64) error
}

// Store owns the optional organization provider model.
type Store struct {
	db                            coredb.DB
	core                          CoreBridge
	feishuBaseURL                 string
	feishuHTTPClient              *http.Client
	feishuAppID                   string
	feishuAppSecret               string
	feishuCredentialEncryptionKey []byte
	checkOrganizationQuotaInTx    func(context.Context, coredb.Tx, string, plan.MetricKey, int64) error
}

type TxStore struct {
	tx                            coredb.Tx
	db                            coredb.QueryExecer
	core                          CoreBridge
	feishuBaseURL                 string
	feishuHTTPClient              *http.Client
	feishuAppID                   string
	feishuAppSecret               string
	feishuCredentialEncryptionKey []byte
	checkOrganizationQuotaInTx    func(context.Context, coredb.Tx, string, plan.MetricKey, int64) error
}

// Service is kept as a compatibility alias while callers migrate to Store.
type Service = Store

func NewWithDB(db coredb.DB, core CoreBridge) *Store {
	return NewWithDBOptions(db, core, ServiceOptions{})
}

func NewWithDBOptions(db coredb.DB, core CoreBridge, options ServiceOptions) *Store {
	if db == nil || core == nil {
		return nil
	}
	feishuBaseURL := strings.TrimSpace(options.FeishuBaseURL)
	if feishuBaseURL == "" {
		feishuBaseURL = defaultFeishuBaseURL
	}
	feishuHTTPClient := options.FeishuHTTPClient
	if feishuHTTPClient == nil {
		feishuHTTPClient = &http.Client{Timeout: 10 * time.Second}
	}
	checkOrganizationQuotaInTx := options.CheckOrganizationQuotaInTx
	if checkOrganizationQuotaInTx == nil {
		checkOrganizationQuotaInTx = plan.New(db).CheckOrganizationQuotaInTx
	}
	return &Store{
		db:                            db,
		core:                          core,
		feishuBaseURL:                 feishuBaseURL,
		feishuHTTPClient:              feishuHTTPClient,
		feishuAppID:                   strings.TrimSpace(options.FeishuAppID),
		feishuAppSecret:               strings.TrimSpace(options.FeishuAppSecret),
		feishuCredentialEncryptionKey: append([]byte(nil), options.FeishuCredentialEncryptionKey...),
		checkOrganizationQuotaInTx:    checkOrganizationQuotaInTx,
	}
}

func (store *Store) InTx(tx coredb.Tx) TxStore {
	return TxStore{
		tx:                            tx,
		db:                            tx,
		core:                          store.core,
		feishuBaseURL:                 store.feishuBaseURL,
		feishuHTTPClient:              store.feishuHTTPClient,
		feishuAppID:                   store.feishuAppID,
		feishuAppSecret:               store.feishuAppSecret,
		feishuCredentialEncryptionKey: append([]byte(nil), store.feishuCredentialEncryptionKey...),
		checkOrganizationQuotaInTx:    store.checkOrganizationQuotaInTx,
	}
}

type requestError struct {
	kind    error
	message string
}

func (err requestError) Error() string {
	return err.message
}

func (err requestError) Unwrap() error {
	return err.kind
}

func badRequest(message string) error {
	return requestError{kind: ErrBadRequest, message: message}
}

func conflict(message string) error {
	return requestError{kind: ErrConflict, message: message}
}
