import {
  FilePart,
  ImagePart,
  ProviderOptions,
  ReasoningPart,
  TextPart,
  ToolApprovalRequest,
  ToolApprovalResponse,
  ToolResultOutput,
  ToolResultPart,
} from '@ai-sdk/provider-utils';
import { z } from 'zod/v4';
import { jsonValueSchema } from '../types/json-value';
import { providerMetadataSchema } from '../types/provider-metadata';
import { dataContentSchema } from './data-content';

/**
 * @internal
 */
export const textPartSchema: z.ZodType<TextPart> = z.object({
  type: z.literal('text'),
  text: z.string(),
  providerOptions: providerMetadataSchema.optional(),
});

/**
 * @internal
 */
export const imagePartSchema: z.ZodType<ImagePart> = z.object({
  type: z.literal('image'),
  image: z.union([dataContentSchema, z.instanceof(URL)]),
  mediaType: z.string().optional(),
  providerOptions: providerMetadataSchema.optional(),
});

/**
 * @internal
 */
export const filePartSchema: z.ZodType<FilePart> = z.object({
  type: z.literal('file'),
  data: z.union([dataContentSchema, z.instanceof(URL)]),
  filename: z.string().optional(),
  mediaType: z.string(),
  providerOptions: providerMetadataSchema.optional(),
});

/**
 * @internal
 */
export const reasoningPartSchema: z.ZodType<ReasoningPart> = z.object({
  type: z.literal('reasoning'),
  text: z.string(),
  providerOptions: providerMetadataSchema.optional(),
});

/**
 * Tool call content part of a prompt. It contains a tool call (usually generated by the AI model).
 */
export interface ToolCallPart {
  type: 'tool-call';

  /**
   * ID of the tool call. This ID is used to match the tool call with the tool result.
   */
  toolCallId: string;

  /**
   * Name of the tool that is being called.
   */
  toolName: string;

  /**
   * Arguments of the tool call. This is a JSON-serializable object that matches the tool's input schema.
   */
  input: unknown;

  /**
   * Additional provider-specific metadata. They are passed through
   * to the provider from the AI SDK and enable provider-specific
   * functionality that can be fully encapsulated in the provider.
   */
  providerOptions?: ProviderOptions;
}

/**
 * @internal
 */
export const toolCallPartSchema: z.ZodType<ToolCallPart> = z.object({
  type: z.literal('tool-call'),
  toolCallId: z.string(),
  toolName: z.string(),
  input: z.unknown(),
  providerOptions: providerMetadataSchema.optional(),
  providerExecuted: z.boolean().optional(),
}) as z.ZodType<ToolCallPart>; // necessary bc input is optional on Zod type

/**
 * @internal
 */
export const outputSchema: z.ZodType<ToolResultOutput> = z.discriminatedUnion(
  'type',
  [
    z.object({
      type: z.literal('text'),
      value: z.string(),
      providerOptions: providerMetadataSchema.optional(),
    }),
    z.object({
      type: z.literal('json'),
      value: jsonValueSchema,
      providerOptions: providerMetadataSchema.optional(),
    }),
    z.object({
      type: z.literal('execution-denied'),
      reason: z.string().optional(),
      providerOptions: providerMetadataSchema.optional(),
    }),
    z.object({
      type: z.literal('error-text'),
      value: z.string(),
      providerOptions: providerMetadataSchema.optional(),
    }),
    z.object({
      type: z.literal('error-json'),
      value: jsonValueSchema,
      providerOptions: providerMetadataSchema.optional(),
    }),
    z.object({
      type: z.literal('content'),
      value: z.array(
        z.union([
          z.object({
            type: z.literal('text'),
            text: z.string(),
            providerOptions: providerMetadataSchema.optional(),
          }),
          z.object({
            type: z.literal('media'),
            data: z.string(),
            mediaType: z.string(),
          }),
          z.object({
            type: z.literal('file-data'),
            data: z.string(),
            mediaType: z.string(),
            filename: z.string().optional(),
            providerOptions: providerMetadataSchema.optional(),
          }),
          z.object({
            type: z.literal('file-url'),
            url: z.string(),
            providerOptions: providerMetadataSchema.optional(),
          }),
          z.object({
            type: z.literal('file-id'),
            fileId: z.union([z.string(), z.record(z.string(), z.string())]),
            providerOptions: providerMetadataSchema.optional(),
          }),
          z.object({
            type: z.literal('image-data'),
            data: z.string(),
            mediaType: z.string(),
            providerOptions: providerMetadataSchema.optional(),
          }),
          z.object({
            type: z.literal('image-url'),
            url: z.string(),
            providerOptions: providerMetadataSchema.optional(),
          }),
          z.object({
            type: z.literal('image-file-id'),
            fileId: z.union([z.string(), z.record(z.string(), z.string())]),
            providerOptions: providerMetadataSchema.optional(),
          }),
          z.object({
            type: z.literal('custom'),
            providerOptions: providerMetadataSchema.optional(),
          }),
        ]),
      ),
    }),
  ],
);

/**
 * @internal
 */
export const toolResultPartSchema: z.ZodType<ToolResultPart> = z.object({
  type: z.literal('tool-result'),
  toolCallId: z.string(),
  toolName: z.string(),
  output: outputSchema,
  providerOptions: providerMetadataSchema.optional(),
}) as z.ZodType<ToolResultPart>; // necessary bc result is optional on Zod type

/**
 * @internal
 */
export const toolApprovalRequestSchema: z.ZodType<ToolApprovalRequest> =
  z.object({
    type: z.literal('tool-approval-request'),
    approvalId: z.string(),
    toolCallId: z.string(),
  });

/**
 * @internal
 */
export const toolApprovalResponseSchema: z.ZodType<ToolApprovalResponse> =
  z.object({
    type: z.literal('tool-approval-response'),
    approvalId: z.string(),
    approved: z.boolean(),
    reason: z.string().optional(),
  });
