import { z } from 'zod'
import {
  projectChannelSchema,
  type ProjectChannel
} from './assistant-contracts'

export const CHANNEL_SETTINGS_LIMITS = {
  maximumIdentifierLength: 256,
  maximumSecretLength: 4_096,
  maximumAllowedSenders: 100,
  maximumStatusMessageLength: 500,
  maximumWarningLength: 500
} as const

export const managedChannelSchema = projectChannelSchema
export type ManagedChannel = ProjectChannel
export const credentialChannelSchema = z.enum(['wecom', 'dingtalk'])
export type CredentialChannel = z.infer<
  typeof credentialChannelSchema
>

const identifierSchema = z
  .string()
  .trim()
  .max(CHANNEL_SETTINGS_LIMITS.maximumIdentifierLength)

const senderIdentifierSchema = identifierSchema.min(1)

export const allowedSenderIdsSchema = z
  .array(senderIdentifierSchema)
  .max(CHANNEL_SETTINGS_LIMITS.maximumAllowedSenders)
  .transform((values) => [...new Set(values)])

export const channelSecretUpdateSchema = z.discriminatedUnion('action', [
  z.object({ action: z.literal('keep') }).strict(),
  z
    .object({
      action: z.literal('replace'),
      value: z
        .string()
        .trim()
        .min(1)
        .max(CHANNEL_SETTINGS_LIMITS.maximumSecretLength)
    })
    .strict(),
  z.object({ action: z.literal('clear') }).strict()
])
export type ChannelSecretUpdate = z.infer<
  typeof channelSecretUpdateSchema
>

const editableChannelFields = {
  enabled: z.boolean(),
  secret: channelSecretUpdateSchema,
  allowedSenderIds: allowedSenderIdsSchema,
  allowGroupMessages: z.boolean()
} as const

export const weComChannelSettingsInputSchema = z
  .object({
    ...editableChannelFields,
    botId: identifierSchema
  })
  .strict()
export type WeComChannelSettingsInput = z.infer<
  typeof weComChannelSettingsInputSchema
>

export const dingTalkChannelSettingsInputSchema = z
  .object({
    ...editableChannelFields,
    clientId: identifierSchema
  })
  .strict()
export type DingTalkChannelSettingsInput = z.infer<
  typeof dingTalkChannelSettingsInputSchema
>

export const weixinChannelSettingsInputSchema = z
  .object({
    enabled: z.boolean()
  })
  .strict()
export type WeixinChannelSettingsInput = z.infer<
  typeof weixinChannelSettingsInputSchema
>

export const channelSettingsApplySchema = z
  .object({
    weixin: weixinChannelSettingsInputSchema.optional(),
    wecom: weComChannelSettingsInputSchema.optional(),
    dingtalk: dingTalkChannelSettingsInputSchema.optional()
  })
  .strict()
  .refine(
    (input) =>
      input.weixin !== undefined ||
      input.wecom !== undefined ||
      input.dingtalk !== undefined,
    '至少需要提供一个通道设置'
  )
export type ChannelSettingsApply = z.infer<
  typeof channelSettingsApplySchema
>

export const channelCredentialSourceSchema = z.enum([
  'none',
  'encrypted',
  'environment'
])
export type ChannelCredentialSource = z.infer<
  typeof channelCredentialSourceSchema
>

export const channelRuntimeStateSchema = z.enum([
  'disabled',
  'stopped',
  'starting',
  'running',
  'error'
])
export type ChannelRuntimeState = z.infer<
  typeof channelRuntimeStateSchema
>

export const channelRuntimeStatusSchema = z
  .object({
    state: channelRuntimeStateSchema,
    lastError: z
      .string()
      .trim()
      .min(1)
      .max(CHANNEL_SETTINGS_LIMITS.maximumStatusMessageLength)
      .optional()
  })
  .strict()
export type ChannelRuntimeStatus = z.infer<
  typeof channelRuntimeStatusSchema
>

const publicChannelFields = {
  enabled: z.boolean(),
  secretConfigured: z.boolean(),
  source: channelCredentialSourceSchema,
  readOnly: z.boolean(),
  allowedSenderIds: allowedSenderIdsSchema,
  allowGroupMessages: z.boolean(),
  status: channelRuntimeStatusSchema
} as const

export const weComChannelSettingsSchema = z
  .object({
    ...publicChannelFields,
    botId: identifierSchema
  })
  .strict()
export type WeComChannelSettings = z.infer<
  typeof weComChannelSettingsSchema
>

export const dingTalkChannelSettingsSchema = z
  .object({
    ...publicChannelFields,
    clientId: identifierSchema
  })
  .strict()
export type DingTalkChannelSettings = z.infer<
  typeof dingTalkChannelSettingsSchema
>

export const weixinChannelSettingsSchema = z
  .object({
    enabled: z.boolean(),
    bindingConfigured: z.boolean(),
    source: z.enum(['none', 'encrypted']),
    accountDisplay: z.string().trim().min(1).max(64).optional(),
    status: channelRuntimeStatusSchema
  })
  .strict()
export type WeixinChannelSettings = z.infer<
  typeof weixinChannelSettingsSchema
>

export const channelSettingsSnapshotSchema = z
  .object({
    weixin: weixinChannelSettingsSchema,
    wecom: weComChannelSettingsSchema,
    dingtalk: dingTalkChannelSettingsSchema,
    warning: z
      .string()
      .trim()
      .min(1)
      .max(CHANNEL_SETTINGS_LIMITS.maximumWarningLength)
      .optional()
  })
  .strict()
export type ChannelSettingsSnapshot = z.infer<
  typeof channelSettingsSnapshotSchema
>

export const channelConnectionTestResultSchema = z
  .object({
    channel: managedChannelSchema,
    ok: z.boolean(),
    error: z
      .string()
      .trim()
      .min(1)
      .max(CHANNEL_SETTINGS_LIMITS.maximumStatusMessageLength)
      .optional()
  })
  .strict()
  .superRefine((result, context) => {
    if (result.ok && result.error !== undefined) {
      context.addIssue({
        code: 'custom',
        path: ['error'],
        message: '成功结果不能包含错误'
      })
    }
    if (!result.ok && result.error === undefined) {
      context.addIssue({
        code: 'custom',
        path: ['error'],
        message: '失败结果必须包含错误'
      })
    }
  })
export type ChannelConnectionTestResult = z.infer<
  typeof channelConnectionTestResultSchema
>
