import { v } from 'convex/values';

import {
  jsonValueValidator,
  jsonRecordValidator,
} from '../../lib/shared/schemas/utils/json-value';
import { internalMutation } from '../_generated/server';
import { createIntegrationInternal as createIntegrationHelper } from './create_integration_internal';
import {
  statusValidator,
  apiKeyAuthEncryptedValidator,
  basicAuthEncryptedValidator,
  oauth2AuthEncryptedValidator,
  oauth2ConfigStoredValidator,
  connectionConfigValidator,
  capabilitiesValidator,
  connectorConfigValidator,
  sqlConnectionConfigValidator,
  sqlOperationValidator,
} from './validators';

const authMethodValidator = v.union(
  v.literal('api_key'),
  v.literal('bearer_token'),
  v.literal('basic_auth'),
  v.literal('oauth2'),
);

const typeValidator = v.union(v.literal('rest_api'), v.literal('sql'));

export const createIntegration = internalMutation({
  args: {
    organizationId: v.string(),
    name: v.string(),
    title: v.string(),
    description: v.optional(v.string()),
    status: statusValidator,
    isActive: v.boolean(),
    authMethod: authMethodValidator,
    supportedAuthMethods: v.optional(v.array(authMethodValidator)),
    apiKeyAuth: v.optional(
      v.object({
        keyEncrypted: v.string(),
        headerName: v.optional(v.string()),
        prefix: v.optional(v.string()),
      }),
    ),
    basicAuth: v.optional(
      v.object({
        username: v.string(),
        passwordEncrypted: v.string(),
      }),
    ),
    oauth2Auth: v.optional(
      v.object({
        accessTokenEncrypted: v.string(),
        refreshTokenEncrypted: v.optional(v.string()),
        tokenExpiry: v.optional(v.number()),
        scopes: v.optional(v.array(v.string())),
      }),
    ),
    oauth2Config: v.optional(oauth2ConfigStoredValidator),
    connectionConfig: v.optional(jsonRecordValidator),
    capabilities: v.optional(
      v.object({
        canSync: v.optional(v.boolean()),
        canPush: v.optional(v.boolean()),
        canWebhook: v.optional(v.boolean()),
        syncFrequency: v.optional(v.string()),
      }),
    ),
    connector: v.optional(connectorConfigValidator),
    type: v.optional(typeValidator),
    sqlConnectionConfig: v.optional(sqlConnectionConfigValidator),
    sqlOperations: v.optional(v.array(sqlOperationValidator)),
    iconStorageId: v.optional(v.id('_storage')),
    metadata: v.optional(jsonValueValidator),
  },
  handler: async (ctx, args) => {
    return await createIntegrationHelper(ctx, args);
  },
});

export const patchIntegrationMetadata = internalMutation({
  args: {
    integrationId: v.id('integrations'),
    metadataPatch: jsonRecordValidator,
  },
  handler: async (ctx, args) => {
    const integration = await ctx.db.get(args.integrationId);
    if (!integration) {
      throw new Error('Integration not found');
    }

    const currentMetadata =
      integration.metadata &&
      typeof integration.metadata === 'object' &&
      !Array.isArray(integration.metadata)
        ? integration.metadata
        : {};
    const merged = { ...currentMetadata, ...args.metadataPatch };

    await ctx.db.patch(args.integrationId, { metadata: merged });
  },
});

export const updateIntegration = internalMutation({
  args: {
    integrationId: v.id('integrations'),
    title: v.optional(v.string()),
    description: v.optional(v.string()),
    authMethod: v.optional(authMethodValidator),
    supportedAuthMethods: v.optional(v.array(authMethodValidator)),
    status: v.optional(statusValidator),
    isActive: v.optional(v.boolean()),
    apiKeyAuth: v.optional(apiKeyAuthEncryptedValidator),
    basicAuth: v.optional(basicAuthEncryptedValidator),
    oauth2Auth: v.optional(oauth2AuthEncryptedValidator),
    oauth2Config: v.optional(oauth2ConfigStoredValidator),
    connectionConfig: v.optional(connectionConfigValidator),
    connector: v.optional(connectorConfigValidator),
    sqlConnectionConfig: v.optional(sqlConnectionConfigValidator),
    sqlOperations: v.optional(v.array(sqlOperationValidator)),
    capabilities: v.optional(capabilitiesValidator),
    errorMessage: v.optional(v.string()),
    metadata: v.optional(jsonRecordValidator),
  },
  handler: async (ctx, args) => {
    const { integrationId, ...updates } = args;

    const cleanUpdates = Object.fromEntries(
      Object.entries(updates).filter(([_, value]) => value !== undefined),
    );

    await ctx.db.patch(integrationId, cleanUpdates);
  },
});
