/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the 
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0 
********************************************************************************/

#ifndef CAN_TEST_HELPER_H
#define CAN_TEST_SOCKET_H

#include <iostream>
#include <mutex>
#include <deque>
#include <gtest/gtest.h>
#include <interfaces/can.h>
#include <support/app_control.h>
#include <support/toml.h>
#include <support/component_impl.h>
#include "../sdv_services/core/toml_parser/parser_toml.h"
#include "../sdv_services/can_communication_socket_can/can_com_sockets.h"

class CComTestHelper
{
public:
    bool IsVcanInstalled()
    {
        std::string result = exec("ip addr | grep can");
        if(!result.empty())
        { 
            return true;
        }

        bool isInstalled = false;
        exec("sudo ip link add dev vcan0 type vcan"); // try to create something
        result = exec("ip addr | grep can");
        if(!result.empty())        
        { 
            isInstalled = true;
        }
        exec("sudo ip link delete vcan0"); // revert it again
        
        return isInstalled;
    }

    struct PcloseDeleter 
    {
        void operator()(FILE* file) const 
        {
            pclose(file);
        }
    };
    
    std::string exec(const char* cmd) 
    {
        std::array<char, 128> buffer;
        std::string result;
        std::unique_ptr<FILE, PcloseDeleter> pipe(popen(cmd, "r"));
        if (!pipe) 
        {
            return "";
        }
        while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) 
        {
            result += buffer.data();
        }
        return result;
    }    

    bool ValidateVCanSetup(const std::string& name, bool expected)
    {
        bool found = false;
        struct ifaddrs *ifaddr, *ifa;
        if (getifaddrs(&ifaddr) != -1)
        {
            for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
            {
                if (name.compare(ifa->ifa_name) == 0)
                {
                    found = true;
                    break;
                }
            }
        }
        freeifaddrs(ifaddr);

        if (found == expected)
        {
            return true;
        }

        std::cout << "VCan setup: Interface " << name << " expected to be = " << std::to_string(expected) 
            << " but is = " << std::to_string(found) << std::endl;
        return false;
    }

    sdv::can::SMessage CreateTestData(uint8_t init, uint8_t dataSize)
    {
        uint8_t value = init;
        sdv::can::SMessage testMsg;
        testMsg.uiID = value;  
        testMsg.bCanFd = false;
        testMsg.bExtended = false;
        testMsg.seqData.resize(dataSize);
        for (uint8_t i = 0; i < dataSize; i++)
        {
            testMsg.seqData[i] = value;
            value += 2;
        }
        return testMsg;
    }
    sdv::can::SMessage CreateTestData2(uint8_t init, uint8_t dataSize)
    {
        uint8_t value = init;
        sdv::can::SMessage testMsg;
        testMsg.uiID = value;  
        testMsg.bCanFd = false;
        testMsg.bExtended = false;
        testMsg.seqData.resize(dataSize);
        for (uint8_t i = 0; i < dataSize; i++)
        {
            testMsg.seqData[i] = value;
            if(i==2)testMsg.seqData[i] = 7;
            value += 2;
        }
        return testMsg;
    }

    bool ValidateInterfaces(const sdv::sequence<sdv::u8string>& seqExpectedInterfaces, const sdv::sequence<sdv::u8string>& seqInterfaces)
    {
        bool success = true;
        if (seqExpectedInterfaces.size() != seqInterfaces.size())
        {
            std::cout << "Interfaces failure, expected: " << std::to_string(seqExpectedInterfaces.size()) << " got: " << std::to_string(seqInterfaces.size()) << std::endl;
            success = false;
        }
        else
        {            
            for (size_t nIndex = 0; nIndex < seqInterfaces.size(); nIndex++)
            {
                if (seqExpectedInterfaces[nIndex].compare(seqInterfaces[nIndex]) != 0)
                {
                    success = false,
                    std::cout << "Interface mismatch, expected: " << seqExpectedInterfaces[nIndex].c_str() << " got: " <<  seqInterfaces[nIndex].c_str() << std::endl;
                }
            }
        }

        return success;
    }

    bool ValidateReceivedMessages(const std::deque<std::pair<std::string, sdv::can::SMessage>>& received,
        const sdv::can::SMessage& testMessage, const std::string& interfaceName, size_t expectedSize)
    {
        uint32_t error = 0;
        if (expectedSize == 0)
            error++;

        if (received.size() != expectedSize)
            error++;  

        for (auto message : received)
        {
            if (interfaceName.compare(message.first) != 0)
                error++;

            if (message.second.uiID != testMessage.uiID)
                error++;
   
            if (message.second.seqData.size() != testMessage.seqData.size())
                error++;               
        }

        if (error)
        {
            std::cout << "Got " << std::to_string(error) << " errors from validation of " 
                      << interfaceName << ", expect " << std::to_string(expectedSize) 
                      << " messages, got " << std::to_string(received.size()) << std::endl;
            return false;
        }        
        return true;     
    } 
    
    std::string NormalizeWhitespace(const std::string& str)
    {
        std::string trimmed = str;
        trimmed.erase(trimmed.find_last_not_of(" \t\n\r") + 1); // Trim trailing whitespace
        trimmed.erase(0, trimmed.find_first_not_of(" \t\n\r")); // Trim leading whitespace
        std::string result;
        std::unique_copy(trimmed.begin(), trimmed.end(), std::back_inserter(result),
                            [](char a, char b) { return std::isspace(a) && std::isspace(b); });
        return result;
    }
};

#endif // ! defined CAN_TEST_HELPER_H
