import { isNonNullish } from "@fern-api/ui-core-utils";
import { mapValues } from "es-toolkit";
import { UnreachableCaseError } from "ts-essentials";
import { BaseOpenApiV3_1ConverterNode, } from "../../../BaseOpenApiV3_1Converter.node";
import { resolveExampleReference } from "../../../utils/3.1/resolveExampleReference";
import { resolveSchemaReference } from "../../../utils/3.1/resolveSchemaReference";
import { maybeSingleValueToArray } from "../../../utils/maybeSingleValueToArray";
import { MediaType } from "../../../utils/MediaType";
import { singleUndefinedArrayIfNullOrEmpty } from "../../../utils/singleUndefinedArrayIfNullOrEmpty";
import { singleUndefinedRecordIfNullOrEmpty } from "../../../utils/singleUndefinedRecordIfNullOrEmpty";
import { SchemaConverterNode } from "../../schemas/SchemaConverter.node";
import { ExampleObjectConverterNode, GLOBAL_EXAMPLE_NAME, } from "../ExampleObjectConverter.node";
function matchExampleName(exampleName, requestExampleName) {
    return (exampleName === requestExampleName ||
        requestExampleName === GLOBAL_EXAMPLE_NAME ||
        exampleName === GLOBAL_EXAMPLE_NAME);
}
export class ResponseMediaTypeObjectConverterNode extends BaseOpenApiV3_1ConverterNode {
    streamingFormat;
    path;
    statusCode;
    requests;
    shapes;
    schema;
    contentType;
    unsupportedContentType;
    contentSubtype;
    examples;
    constructor(args, contentType, streamingFormat, path, statusCode, requests, shapes) {
        super(args);
        this.streamingFormat = streamingFormat;
        this.path = path;
        this.statusCode = statusCode;
        this.requests = requests;
        this.shapes = shapes;
        this.safeParse(contentType);
    }
    isExampleDefined(example) {
        return (example != null &&
            resolveExampleReference(example, this.context.document)?.value != null);
    }
    parse(contentType) {
        const mediaType = MediaType.parse(contentType);
        if (mediaType?.isJSON() || mediaType?.isEventStream()) {
            this.contentType = "application/json";
            if (this.input.schema == null) {
                if (this.streamingFormat == null || this.streamingFormat === "json") {
                    this.context.errors.error({
                        message: "Expected schema for JSON response body. Received null",
                        path: this.accessPath,
                    });
                }
            }
            else {
                this.schema = new SchemaConverterNode({
                    input: this.input.schema,
                    context: this.context,
                    accessPath: this.accessPath,
                    pathId: "type",
                    seenSchemas: new Set(),
                });
            }
        }
        else if (mediaType?.isOctetStream()) {
            this.contentType = "application/octet-stream";
            this.contentSubtype = resolveSchemaReference(this.input.schema, this.context.document)?.contentMediaType;
        }
        else {
            this.unsupportedContentType = contentType;
            if (this.input.schema == null) {
                this.context.errors.error({
                    message: "Expected schema for plain text response body. Received null",
                    path: this.accessPath,
                });
                return;
            }
            else {
                this.schema = new SchemaConverterNode({
                    input: this.input.schema,
                    context: this.context,
                    accessPath: this.accessPath,
                    pathId: this.pathId,
                    seenSchemas: new Set(),
                });
            }
        }
        let responseExamples = {};
        if (this.input.example != null) {
            responseExamples[GLOBAL_EXAMPLE_NAME] = [
                {
                    value: this.input.example,
                },
            ];
        }
        if (this.input.examples != null) {
            responseExamples = {
                ...responseExamples,
                ...mapValues(this.input.examples, (v) => [v]),
            };
        }
        if (this.contentType != null || this.unsupportedContentType != null) {
            const resolvedSchema = resolveSchemaReference(this.input.schema, this.context.document);
            if (resolvedSchema != null) {
                if (resolvedSchema.example != null) {
                    responseExamples[GLOBAL_EXAMPLE_NAME] ??= [];
                    responseExamples[GLOBAL_EXAMPLE_NAME]?.push({
                        value: resolvedSchema.example,
                    });
                }
                if (resolvedSchema.examples != null) {
                    responseExamples[GLOBAL_EXAMPLE_NAME] ??= [];
                    responseExamples[GLOBAL_EXAMPLE_NAME]?.push(...resolvedSchema.examples.map((v) => ({
                        value: v,
                    })));
                }
            }
            responseExamples[GLOBAL_EXAMPLE_NAME] ??= [];
            responseExamples[GLOBAL_EXAMPLE_NAME]?.push({
                value: this.schema?.example(),
            });
        }
        this.examples = singleUndefinedArrayIfNullOrEmpty(this.requests).flatMap((request) => Object.entries(singleUndefinedRecordIfNullOrEmpty(request?.examples)).flatMap(([requestExampleName, requestExample]) => Object.entries(responseExamples)
            .flatMap(([exampleName, examples]) => examples.map((responseExample) => matchExampleName(exampleName, requestExampleName) &&
            (this.isExampleDefined(requestExample) ||
                this.isExampleDefined(responseExample))
            ? new ExampleObjectConverterNode({
                input: {
                    requestExample,
                    responseExample,
                },
                context: this.context,
                accessPath: this.accessPath,
                pathId: ["examples", ""],
            }, this.path, this.statusCode, exampleName === "" ? undefined : exampleName, {
                requestBody: request,
                responseBody: this,
                pathParameters: this.shapes.pathParameters,
                queryParameters: this.shapes.queryParameters,
                requestHeaders: this.shapes.requestHeaders,
            })
            : undefined))
            .filter(isNonNullish)));
    }
    convertStreamingFormat() {
        switch (this.streamingFormat) {
            case "json": {
                const maybeShapes = maybeSingleValueToArray(this.schema?.convert());
                return maybeShapes?.map((shape) => ({
                    type: "stream",
                    // TODO: Parse terminator (probably extension)
                    terminator: "[DATA]",
                    shape,
                }));
            }
            case "sse":
                return { type: "streamingText" };
            case undefined:
                return undefined;
            default:
                new UnreachableCaseError(this.streamingFormat);
                return undefined;
        }
    }
    convert() {
        if (this.contentType != null) {
            switch (this.contentType) {
                case "application/json":
                    if (this.streamingFormat == null) {
                        const maybeShapes = maybeSingleValueToArray(this.schema?.convert());
                        return maybeShapes
                            ?.map((shape) => {
                            if (shape == null ||
                                (shape.type !== "object" && shape.type !== "alias")) {
                                return undefined;
                            }
                            return shape;
                        })
                            .filter(isNonNullish);
                    }
                    else {
                        return this.convertStreamingFormat();
                    }
                case "application/octet-stream":
                    return { type: "fileDownload", contentType: this.contentSubtype };
                case "text/event-stream":
                    return this.convertStreamingFormat();
                case undefined:
                    return undefined;
                default:
                    new UnreachableCaseError(this.contentType);
                    return undefined;
            }
        }
        else if (this.unsupportedContentType != null) {
            const maybeShapes = maybeSingleValueToArray(this.schema?.convert());
            return maybeShapes
                ?.map((shape) => {
                const type = shape.type;
                switch (type) {
                    case "alias":
                        return shape;
                    case "discriminatedUnion":
                    case "undiscriminatedUnion":
                    case "enum":
                        return undefined;
                    case "object":
                        return shape;
                    default:
                        new UnreachableCaseError(type);
                        return undefined;
                }
            })
                .filter(isNonNullish);
        }
        else {
            return undefined;
        }
    }
}
