package com.cameleer.server.app.storage;

import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.app.TestSecurityHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Integration test verifying that processor execution data is correctly populated
 * during ingestion of route executions with nested processors and exchange data.
 */
class IngestionSchemaIT extends AbstractPostgresIT {

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private TestSecurityHelper securityHelper;

    private HttpHeaders authHeaders;

    @BeforeEach
    void setUp() {
        String jwt = securityHelper.registerTestAgent("test-agent-ingestion-schema-it");
        authHeaders = securityHelper.authHeaders(jwt);
    }

    @Test
    void processorTreeMetadata_depthsAndParentIdsCorrect() {
        String json = """
                {
                  "routeId": "schema-test-tree",
                  "exchangeId": "ex-tree-1",
                  "correlationId": "corr-tree-1",
                  "status": "COMPLETED",
                  "startTime": "2026-03-11T10:00:00Z",
                  "endTime": "2026-03-11T10:00:01Z",
                  "durationMs": 1000,
                  "processors": [
                    {
                      "processorId": "root-proc",
                      "processorType": "bean",
                      "status": "COMPLETED",
                      "startTime": "2026-03-11T10:00:00Z",
                      "endTime": "2026-03-11T10:00:00.500Z",
                      "durationMs": 500,
                                            "inputBody": "root-input",
                      "outputBody": "root-output",
                      "inputHeaders": {"Content-Type": "application/json"},
                      "outputHeaders": {"X-Result": "ok"},
                      "children": [
                        {
                          "processorId": "child-proc",
                          "processorType": "log",
                          "status": "COMPLETED",
                          "startTime": "2026-03-11T10:00:00.100Z",
                          "endTime": "2026-03-11T10:00:00.400Z",
                          "durationMs": 300,
                                                    "inputBody": "child-input",
                          "outputBody": "child-output",
                          "children": [
                            {
                              "processorId": "grandchild-proc",
                              "processorType": "setHeader",
                              "status": "COMPLETED",
                              "startTime": "2026-03-11T10:00:00.200Z",
                              "endTime": "2026-03-11T10:00:00.300Z",
                              "durationMs": 100,
                                                            "children": []
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
                """;

        postExecution(json);

        // Verify execution row exists
        Integer execCount = jdbcTemplate.queryForObject(
                "SELECT count(*) FROM executions WHERE execution_id = 'ex-tree-1'",
                Integer.class);
        assertThat(execCount).isEqualTo(1);

        // Verify processors were flattened into processor_executions
        List<Map<String, Object>> processors = jdbcTemplate.queryForList(
                "SELECT processor_id, processor_type, depth, parent_processor_id, " +
                "input_body, output_body, input_headers " +
                "FROM processor_executions WHERE execution_id = 'ex-tree-1' " +
                "ORDER BY depth, processor_id");
        assertThat(processors).hasSize(3);

        // Root processor: depth=0, no parent
        assertThat(processors.get(0).get("processor_id")).isEqualTo("root-proc");
        assertThat(((Number) processors.get(0).get("depth")).intValue()).isEqualTo(0);
        assertThat(processors.get(0).get("parent_processor_id")).isNull();
        assertThat(processors.get(0).get("input_body")).isEqualTo("root-input");
        assertThat(processors.get(0).get("output_body")).isEqualTo("root-output");
        assertThat(processors.get(0).get("input_headers").toString()).contains("Content-Type");

        // Child processor: depth=1, parent=root-proc
        assertThat(processors.get(1).get("processor_id")).isEqualTo("child-proc");
        assertThat(((Number) processors.get(1).get("depth")).intValue()).isEqualTo(1);
        assertThat(processors.get(1).get("parent_processor_id")).isEqualTo("root-proc");
        assertThat(processors.get(1).get("input_body")).isEqualTo("child-input");
        assertThat(processors.get(1).get("output_body")).isEqualTo("child-output");

        // Grandchild processor: depth=2, parent=child-proc
        assertThat(processors.get(2).get("processor_id")).isEqualTo("grandchild-proc");
        assertThat(((Number) processors.get(2).get("depth")).intValue()).isEqualTo(2);
        assertThat(processors.get(2).get("parent_processor_id")).isEqualTo("child-proc");
    }

    @Test
    void exchangeBodiesStored() {
        String json = """
                {
                  "routeId": "schema-test-bodies",
                  "exchangeId": "ex-bodies-1",
                  "status": "COMPLETED",
                  "startTime": "2026-03-11T10:00:00Z",
                  "endTime": "2026-03-11T10:00:01Z",
                  "durationMs": 1000,
                  "processors": [
                    {
                      "processorId": "proc-1",
                      "processorType": "bean",
                      "status": "COMPLETED",
                      "startTime": "2026-03-11T10:00:00Z",
                      "endTime": "2026-03-11T10:00:00.500Z",
                      "durationMs": 500,
                      "inputBody": "processor-body-text",
                      "outputBody": "processor-output-text",
                      "children": []
                    }
                  ]
                }
                """;

        postExecution(json);

        // Verify processor body data
        List<Map<String, Object>> processors = jdbcTemplate.queryForList(
                "SELECT input_body, output_body FROM processor_executions " +
                "WHERE execution_id = 'ex-bodies-1'");
        assertThat(processors).hasSize(1);
        assertThat(processors.get(0).get("input_body")).isEqualTo("processor-body-text");
        assertThat(processors.get(0).get("output_body")).isEqualTo("processor-output-text");
    }

    @Test
    void nullSnapshots_insertSucceedsWithEmptyDefaults() {
        String json = """
                {
                  "routeId": "schema-test-null-snap",
                  "exchangeId": "ex-null-1",
                  "status": "COMPLETED",
                  "startTime": "2026-03-11T10:00:00Z",
                  "endTime": "2026-03-11T10:00:01Z",
                  "durationMs": 1000,
                  "processors": [
                    {
                      "processorId": "proc-null",
                      "processorType": "log",
                      "status": "COMPLETED",
                      "startTime": "2026-03-11T10:00:00Z",
                      "endTime": "2026-03-11T10:00:00.500Z",
                      "durationMs": 500,
                      "children": []
                    }
                  ]
                }
                """;

        postExecution(json);

        // Verify execution exists
        Integer count = jdbcTemplate.queryForObject(
                "SELECT count(*) FROM executions WHERE execution_id = 'ex-null-1'",
                Integer.class);
        assertThat(count).isEqualTo(1);

        // Verify processor with null bodies inserted successfully
        List<Map<String, Object>> processors = jdbcTemplate.queryForList(
                "SELECT depth, parent_processor_id, input_body, output_body " +
                "FROM processor_executions WHERE execution_id = 'ex-null-1'");
        assertThat(processors).hasSize(1);
        assertThat(((Number) processors.get(0).get("depth")).intValue()).isEqualTo(0);
        assertThat(processors.get(0).get("parent_processor_id")).isNull();
    }

    private void postExecution(String json) {
        ResponseEntity<String> response = restTemplate.postForEntity(
                "/api/v1/data/executions",
                new HttpEntity<>(json, authHeaders),
                String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED);
    }
}
