import { ApId, BaseModelSchema, DateOrString, Nullable } from '@activepieces/core-utils'
import { z } from 'zod'
import { FederatedAuthnProviderConfig, FederatedAuthnProviderConfigWithoutSensitiveData } from '../../core/federated-authn'
import { SsoDomainVerification } from './sso-domain-verification'

export enum FilteredPieceBehavior {
    ALLOWED = 'ALLOWED',
    BLOCKED = 'BLOCKED',
}

export const PlatformUsage = z.object({
    totalAiCreditsUsed: z.number(),
    totalAiCreditsUsedThisMonth: z.number(),
    aiCreditsRemaining: z.number(),
    aiCreditsLimit: z.number(),
    activeFlows: z.number(),
})

export type PlatformUsage = z.infer<typeof PlatformUsage>

export enum PlanName {
    STANDARD = 'standard',
    ENTERPRISE = 'enterprise',
    APPSUMO_ACTIVEPIECES_TIER1 = 'appsumo_activepieces_tier1',
    APPSUMO_ACTIVEPIECES_TIER2 = 'appsumo_activepieces_tier2',
    APPSUMO_ACTIVEPIECES_TIER3 = 'appsumo_activepieces_tier3',
    APPSUMO_ACTIVEPIECES_TIER4 = 'appsumo_activepieces_tier4',
    APPSUMO_ACTIVEPIECES_TIER5 = 'appsumo_activepieces_tier5',
    APPSUMO_ACTIVEPIECES_TIER6 = 'appsumo_activepieces_tier6',
}

export enum TeamProjectsLimit {
    NONE = 'NONE',
    ONE = 'ONE',
    UNLIMITED = 'UNLIMITED',
}

export enum AiCreditsAutoTopUpState {
    ENABLED = 'enabled',
    DISABLED = 'disabled',
}

export const PlatformPlan = z.object({
    ...BaseModelSchema,
    // TODO: We have to use the enum when we finalize the plan names
    plan: Nullable(z.string()),
    platformId: z.string(),
    includedAiCredits: z.number(),
    lastFreeAiCreditsRenewalDate: Nullable(DateOrString),

    tablesEnabled: z.boolean(),
    eventStreamingEnabled: z.boolean(),
    aiCreditsAutoTopUpState: z.nativeEnum(AiCreditsAutoTopUpState),
    aiCreditsAutoTopUpThreshold: Nullable(z.number()),
    aiCreditsAutoTopUpCreditsToAdd: Nullable(z.number()),
    maxAutoTopUpCreditsMonthly: Nullable(z.number()),

    environmentsEnabled: z.boolean(),
    analyticsEnabled: z.boolean(),
    showPoweredBy: z.boolean(),
    auditLogEnabled: z.boolean(),
    embeddingEnabled: z.boolean(),
    agentsEnabled: z.boolean(),
    aiProvidersEnabled: z.boolean(),
    chatEnabled: z.boolean(),
    dataManipulationEnabled: z.boolean(),
    managePiecesEnabled: z.boolean(),
    manageTemplatesEnabled: z.boolean(),
    customAppearanceEnabled: z.boolean(),
    teamProjectsLimit: z.nativeEnum(TeamProjectsLimit),
    projectRolesEnabled: z.boolean(),
    globalConnectionsEnabled: z.boolean(),
    customRolesEnabled: z.boolean(),
    apiKeysEnabled: z.boolean(),
    ssoEnabled: z.boolean(),
    secretManagersEnabled: z.boolean(),
    scimEnabled: z.boolean(),
    licenseKey: Nullable(z.string()),
    licenseExpiresAt: Nullable(DateOrString),
    stripeCustomerId: Nullable(z.string()),
    stripeSubscriptionId: Nullable(z.string()),
    stripeSubscriptionStatus: Nullable(z.string()),
    stripeSubscriptionStartDate: Nullable(z.number()),
    stripeSubscriptionEndDate: Nullable(z.number()),
    stripeSubscriptionCancelDate: Nullable(z.number()),

    projectsLimit: Nullable(z.number()),
    activeFlowsLimit: Nullable(z.number()),

    /** @deprecated use workerGroupId instead — will be removed in 0.83.0 */
    dedicatedWorkers: Nullable(z.object({
        trustedEnvironment: z.boolean(),
    })),
    /** @deprecated use workerGroupId instead — will be removed in 0.83.0 */
    canary: z.boolean(),
    /** @deprecated custom domains have been removed; column kept for backwards compatibility with existing DBs */
    customDomainsEnabled: z.boolean(),
    workerGroupId: Nullable(z.string()),
})
export type PlatformPlan = z.infer<typeof PlatformPlan>

export const PlatformPlanLimits = PlatformPlan.omit({ id: true, platformId: true, created: true, updated: true })
export type PlatformPlanLimits = z.infer<typeof PlatformPlanLimits>
export type PlatformPlanWithOnlyLimits = Omit<PlatformPlanLimits, 'stripeSubscriptionStartDate' | 'stripeSubscriptionEndDate' | 'stripeBillingCycle'>

export const HEX_COLOR_PATTERN = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/

const hexColor = z.string().regex(HEX_COLOR_PATTERN, 'invalidHexColor')

export const PlatformThemeColors = z.object({
    avatar: hexColor.optional(),
    'blue-link': hexColor.optional(),
    danger: hexColor.optional(),
    selection: hexColor.optional(),
    primary: z.object({
        dark: hexColor.optional(),
        light: hexColor.optional(),
        medium: hexColor.optional(),
    }).optional(),
    warn: z.object({
        default: hexColor.optional(),
        light: hexColor.optional(),
        dark: hexColor.optional(),
    }).optional(),
    success: z.object({
        default: hexColor.optional(),
        light: hexColor.optional(),
    }).optional(),
})
export type PlatformThemeColors = z.infer<typeof PlatformThemeColors>

export const PIECE_SELECTOR_BUILTIN_TABS = ['EXPLORE', 'APPS', 'UTILITY', 'AI_AND_AGENTS', 'APPROVALS'] as const

export const PieceSelectorTabSection = z.object({
    id: z.string(),
    title: z.string().min(1).max(40),
    pieceNames: z.array(z.string()),
})
export type PieceSelectorTabSection = z.infer<typeof PieceSelectorTabSection>

export const PieceSelectorTabConfig = z.object({
    id: z.string(),
    kind: z.enum(['BUILTIN', 'CUSTOM']),
    builtinTab: z.enum(PIECE_SELECTOR_BUILTIN_TABS).optional(),
    title: z.string().max(40).optional(),
    icon: z.string().optional(),
    hidden: z.boolean(),
    pieceNames: z.array(z.string()).optional(),
    sections: z.array(PieceSelectorTabSection).optional(),
}).refine(
    (tab) => tab.kind !== 'CUSTOM' || (tab.title?.trim().length ?? 0) > 0,
    { message: 'Custom tabs must have a name', path: ['title'] },
)
export type PieceSelectorTabConfig = z.infer<typeof PieceSelectorTabConfig>

export const PieceSelectorConfig = z.object({
    tabs: z.array(PieceSelectorTabConfig),
})
export type PieceSelectorConfig = z.infer<typeof PieceSelectorConfig>

export const Platform = z.object({
    ...BaseModelSchema,
    ownerId: ApId,
    name: z.string(),
    primaryColor: z.string(),
    themeColors: Nullable(PlatformThemeColors),
    logoIconUrl: z.string(),
    fullLogoUrl: z.string(),
    favIconUrl: z.string(),
    /**
    * @deprecated Use projects filter instead.
    */
    filteredPieceNames: z.array(z.string()),
    /**
    * @deprecated Use projects filter instead.
    */
    filteredPieceBehavior: z.nativeEnum(FilteredPieceBehavior),
    cloudAuthEnabled: z.boolean(),
    googleAuthEnabled: z.boolean(),
    enforceAllowedAuthDomains: z.boolean(),
    allowedAuthDomains: z.array(z.string()),
    allowedEmbedOrigins: z.array(z.string()),
    ssoDomain: Nullable(z.string()),
    ssoDomainVerification: Nullable(SsoDomainVerification),
    federatedAuthProviders: FederatedAuthnProviderConfig,
    emailAuthEnabled: z.boolean(),
    pinnedPieces: z.array(z.string()),
    pieceSelectorConfig: Nullable(PieceSelectorConfig),
})
export type Platform = z.infer<typeof Platform>
export type PlatformWithoutFederatedAuth = Omit<Platform, 'federatedAuthProviders'>

export const PlatformWithoutSensitiveData = z.object({
    federatedAuthProviders: Nullable(FederatedAuthnProviderConfigWithoutSensitiveData),
    plan: PlatformPlanLimits,
    usage: PlatformUsage.optional(),
    id: z.string(),
    created: DateOrString,
    updated: DateOrString,
    ownerId: ApId,
    name: z.string(),
    primaryColor: z.string(),
    themeColors: Nullable(PlatformThemeColors),
    logoIconUrl: z.string(),
    fullLogoUrl: z.string(),
    favIconUrl: z.string(),
    filteredPieceNames: z.array(z.string()),
    filteredPieceBehavior: z.nativeEnum(FilteredPieceBehavior),
    cloudAuthEnabled: z.boolean(),
    googleAuthEnabled: z.boolean(),
    enforceAllowedAuthDomains: z.boolean(),
    allowedAuthDomains: z.array(z.string()),
    allowedEmbedOrigins: z.array(z.string()),
    ssoDomain: Nullable(z.string()),
    ssoDomainVerification: Nullable(SsoDomainVerification),
    emailAuthEnabled: z.boolean(),
    pinnedPieces: z.array(z.string()),
    pieceSelectorConfig: Nullable(PieceSelectorConfig),
})
export type PlatformWithoutSensitiveData = z.infer<typeof PlatformWithoutSensitiveData>

export const PlatformBillingInformation = z.object({
    plan: PlatformPlan,
    usage: PlatformUsage,
    nextBillingDate: z.number(),
    nextBillingAmount: z.number(),
    cancelAt: Nullable(z.number()),
})
export type PlatformBillingInformation = z.infer<typeof PlatformBillingInformation>
