import { BaseTransformer } from '../core/impl/base-transformer.impl';
import { TransformConfig } from '../core/models/model';
import { WrappedAsset } from '../core/models/zip-processor.interface';

describe('Template Processing', () => {
    let transformer: BaseTransformer;

    beforeEach(() => {
        transformer = new BaseTransformer();
    });

    describe('processTemplate method', () => {
        it('should handle undefined values in templates', async () => {
            // Arrange
            const wrappedAsset: WrappedAsset = {
                inputSchema: {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'test',
                        version: '1.0.0'
                    },
                    spec: {
                        existingField: 'exists'
                    }
                },
                metadata: {
                    name: 'test',
                    version: '1.0.0'
                }
            };

            const config: TransformConfig = {
                transformations: {
                    replacements: [
                        {
                            target: '$.Test.value',
                            value: 'Prefix {{$.spec.nonExistentField}} Suffix',
                            precedence: 10,
                            operation: 'replace'
                        }
                    ]
                }
            };

            // Act
            const result = await transformer.transform(wrappedAsset, config);

            // Assert
            expect(result.outputAsset).toEqual({
                Test: {
                    value: 'Prefix  Suffix'
                }
            });
        });

        it('should skip replacements when all template values are undefined', async () => {
            // Arrange
            const wrappedAsset: WrappedAsset = {
                inputSchema: {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'test',
                        version: '1.0.0'
                    },
                    spec: {}
                },
                metadata: {
                    name: 'test',
                    version: '1.0.0'
                }
            };

            const config: TransformConfig = {
                transformations: {
                    replacements: [
                        {
                            target: '$.Test.value',
                            value: '{{$.spec.nonExistentField}}',
                            precedence: 10,
                            operation: 'replace'
                        }
                    ]
                }
            };

            // Act
            const result = await transformer.transform(wrappedAsset, config);

            // Assert
            expect(result.outputAsset).toEqual({});
        });

        it('should handle undefined values in object templates', async () => {
            // Arrange
            const wrappedAsset: WrappedAsset = {
                inputSchema: {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'test',
                        version: '1.0.0'
                    },
                    spec: {
                        existingField: 'exists'
                    }
                },
                metadata: {
                    name: 'test',
                    version: '1.0.0'
                }
            };

            const config: TransformConfig = {
                transformations: {
                    replacements: [
                        {
                            target: '$.Test.object',
                            value: {
                                valid: '{{$.spec.existingField}}',
                                invalid: '{{$.spec.nonExistentField}}'
                            },
                            precedence: 10,
                            operation: 'replace'
                        }
                    ]
                }
            };

            // Act
            const result = await transformer.transform(wrappedAsset, config);

            // Assert
            expect(result.outputAsset).toEqual({
                Test: {
                    object: {
                        valid: 'exists'
                        // invalid field should be omitted
                    }
                }
            });
        });

        it('should handle undefined values in array templates', async () => {
            // Arrange
            const wrappedAsset: WrappedAsset = {
                inputSchema: {
                    kind: 'Test',
                    apiVersion: 'v1',
                    metadata: {
                        name: 'test',
                        version: '1.0.0'
                    },
                    spec: {
                        field1: 'value1',
                        field3: 'value3'
                    }
                },
                metadata: {
                    name: 'test',
                    version: '1.0.0'
                }
            };

            const config: TransformConfig = {
                transformations: {
                    replacements: [
                        {
                            target: '$.Test.array',
                            value: [
                                '{{$.spec.field1}}',
                                '{{$.spec.field2}}', // Doesn't exist
                                '{{$.spec.field3}}'
                            ],
                            precedence: 10,
                            operation: 'replace'
                        }
                    ]
                }
            };

            // Act
            const result = await transformer.transform(wrappedAsset, config);

            // Assert
            expect(result.outputAsset).toEqual({
                Test: {
                    array: [
                        'value1',
                        // field2 should be filtered out
                        'value3'
                    ]
                }
            });
        });
    });

    describe('Multiple asset generation', () => {
        it('should support generating multiple assets from one input', async () => {
            // Arrange
            const wrappedAsset: WrappedAsset = {
                inputSchema: {
                    kind: 'Redact',
                    apiVersion: 'api.ibm.com/v1',
                    metadata: {
                        name: 'redact-request',
                        version: '1.0.0',
                        namespace: 'sample'
                    },
                    spec: {
                        message: {
                            messageName: 'request',
                            queryParameters: ['token', 'trace'],
                            headers: ['X-API-Key', 'X-Auth-Token'],
                            body: {
                                captures: {
                                    jsonata: ['doc.key.password', 'doc.key.user']
                                }
                            }
                        }
                    }
                },
                metadata: {
                    name: 'redact-request',
                    version: '1.0.0',
                    namespace: 'sample'
                }
            };

            const config: TransformConfig = {
                transformations: {
                    replacements: [
                        {
                            target: '$',
                            value: [],
                            precedence: 1,
                            condition: '$.kind === "Redact"'
                        },
                        {
                            target: '$',
                            value: {
                                redact: {
                                    messageQueryParam: {
                                        message: '{{$.spec.message.messageName}}',
                                        paramNames: '{{$.spec.message.queryParameters}}'
                                    }
                                }
                            },
                            operation: 'push',
                            precedence: 2,
                            condition: '$.spec.message && $.spec.message.queryParameters && $.spec.message.queryParameters.length > 0'
                        },
                        {
                            target: '$',
                            value: {
                                redact: {
                                    messageHeader: {
                                        message: '{{$.spec.message.messageName}}',
                                        headerNames: '{{$.spec.message.headers}}'
                                    }
                                }
                            },
                            operation: 'push',
                            precedence: 3,
                            condition: '$.spec.message && $.spec.message.headers && $.spec.message.headers.length > 0'
                        },
                        {
                            target: '$',
                            value: {
                                redact: {
                                    messageBody: {
                                        message: '{{$.spec.message.messageName}}',
                                        captures: {
                                            jsonata: '{{$.spec.message.body.captures.jsonata}}'
                                        }
                                    }
                                }
                            },
                            operation: 'push',
                            precedence: 4,
                            condition: '$.spec.message && $.spec.message.body && $.spec.message.body.captures && $.spec.message.body.captures.jsonata && $.spec.message.body.captures.jsonata.length > 0'
                        }
                    ]
                }
            };

            // Act
            const result = await transformer.transform(wrappedAsset, config);

            // Assert
            expect(Array.isArray(result.outputAsset)).toBe(true);
            expect(result.outputAsset.length).toBe(3);

            // Check that all three expected assets are present
            expect(result.outputAsset).toContainEqual({
                redact: {
                    messageQueryParam: {
                        message: 'request',
                        paramNames: ['token', 'trace']
                    }
                }
            });

            expect(result.outputAsset).toContainEqual({
                redact: {
                    messageHeader: {
                        message: 'request',
                        headerNames: ['X-API-Key', 'X-Auth-Token']
                    }
                }
            });

            expect(result.outputAsset).toContainEqual({
                redact: {
                    messageBody: {
                        message: 'request',
                        captures: {
                            jsonata: ['doc.key.password', 'doc.key.user']
                        }
                    }
                }
            });
        });

        it('should handle non-existent fields in multiple asset generation', async () => {
            // Arrange
            const wrappedAsset: WrappedAsset = {
                inputSchema: {
                    kind: 'Redact',
                    apiVersion: 'api.ibm.com/v1',
                    metadata: {
                        name: 'redact-request',
                        version: '1.0.0',
                        namespace: 'sample'
                    },
                    spec: {
                        message: {
                            messageName: 'request',
                            queryParameters: ['token', 'trace']
                            // No headers or body
                        }
                    }
                },
                metadata: {
                    name: 'redact-request',
                    version: '1.0.0',
                    namespace: 'sample'
                }
            };

            const config: TransformConfig = {
                transformations: {
                    replacements: [
                        {
                            target: '$',
                            value: [],
                            precedence: 1,
                            condition: '$.kind === "Redact"'
                        },
                        {
                            target: '$',
                            value: {
                                redact: {
                                    messageQueryParam: {
                                        message: '{{$.spec.message.messageName}}',
                                        paramNames: '{{$.spec.message.queryParameters}}'
                                    }
                                }
                            },
                            operation: 'push',
                            precedence: 2,
                            condition: '$.spec.message && $.spec.message.queryParameters && $.spec.message.queryParameters.length > 0'
                        },
                        {
                            target: '$',
                            value: {
                                redact: {
                                    messageHeader: {
                                        message: '{{$.spec.message.messageName}}',
                                        headerNames: '{{$.spec.message.headers}}'
                                    }
                                }
                            },
                            operation: 'push',
                            precedence: 3,
                            condition: '$.spec.message && $.spec.message.headers && $.spec.message.headers.length > 0'
                        },
                        {
                            target: '$',
                            value: {
                                redact: {
                                    messageBody: {
                                        message: '{{$.spec.message.messageName}}',
                                        captures: {
                                            jsonata: '{{$.spec.message.body.captures.jsonata}}'
                                        }
                                    }
                                }
                            },
                            operation: 'push',
                            precedence: 4,
                            condition: '$.spec.message && $.spec.message.body && $.spec.message.body.captures && $.spec.message.body.captures.jsonata && $.spec.message.body.captures.jsonata.length > 0'
                        }
                    ]
                }
            };

            // Act
            const result = await transformer.transform(wrappedAsset, config);

            // Assert
            expect(Array.isArray(result.outputAsset)).toBe(true);
            expect(result.outputAsset.length).toBe(1);

            // Only the queryParameters asset should be present
            expect(result.outputAsset).toEqual([
                {
                    redact: {
                        messageQueryParam: {
                            message: 'request',
                            paramNames: ['token', 'trace']
                        }
                    }
                }
            ]);
        });
    });
});

// Made with Bob
