/*
 * Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
 * Copyright (c) 2023-2025, Tim Flynn <trflynn89@ladybird.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/IPv4Address.h>
#include <AK/IPv6Address.h>
#include <AK/JsonValue.h>
#include <AK/Types.h>
#include <AK/Utf16String.h>
#include <LibCore/AnonymousBuffer.h>
#include <LibCore/Proxy.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/File.h>
#include <LibURL/Parser.h>
#include <LibURL/URL.h>

namespace IPC {

// Maximum size for decoded containers (strings, buffers, vectors, etc.)
// This prevents a malicious peer from claiming huge sizes to cause OOM.
static constexpr size_t MAX_DECODED_SIZE = 64 * MiB;

ErrorOr<size_t> Decoder::decode_size()
{
    auto size = static_cast<size_t>(TRY(decode<u32>()));
    if (size > MAX_DECODED_SIZE)
        return Error::from_string_literal("IPC decode: Size exceeds maximum allowed");
    return size;
}

template<>
ErrorOr<String> decode(Decoder& decoder)
{
    auto length = TRY(decoder.decode_size());
    return String::from_stream(decoder.stream(), length);
}

template<>
ErrorOr<Utf16String> decode(Decoder& decoder)
{
    auto is_ascii = TRY(decoder.decode<bool>());
    auto length_in_code_units = TRY(decoder.decode_size());

    return Utf16String::from_ipc_stream(decoder.stream(), length_in_code_units, is_ascii);
}

template<>
ErrorOr<ByteString> decode(Decoder& decoder)
{
    auto length = TRY(decoder.decode_size());
    if (length == 0)
        return ByteString::empty();

    return ByteString::create_and_overwrite(length, [&](Bytes bytes) -> ErrorOr<void> {
        TRY(decoder.decode_into(bytes));
        return {};
    });
}

template<>
ErrorOr<ByteBuffer> decode(Decoder& decoder)
{
    auto length = TRY(decoder.decode_size());
    if (length == 0)
        return ByteBuffer {};

    auto buffer = TRY(ByteBuffer::create_uninitialized(length));
    auto bytes = buffer.bytes();

    TRY(decoder.decode_into(bytes));
    return buffer;
}

template<>
ErrorOr<JsonValue> decode(Decoder& decoder)
{
    auto json = TRY(decoder.decode<ByteString>());
    return JsonValue::from_string(json);
}

template<>
ErrorOr<AK::Duration> decode(Decoder& decoder)
{
    auto nanoseconds = TRY(decoder.decode<i64>());
    return AK::Duration::from_nanoseconds(nanoseconds);
}

template<>
ErrorOr<UnixDateTime> decode(Decoder& decoder)
{
    auto nanoseconds = TRY(decoder.decode<i64>());
    return AK::UnixDateTime::from_nanoseconds_since_epoch(nanoseconds);
}

template<>
ErrorOr<IPv4Address> decode(Decoder& decoder)
{
    auto ipv4 = TRY(decoder.decode<u32>());
    return IPv4Address(ipv4);
}

template<>
ErrorOr<IPv6Address> decode(Decoder& decoder)
{
    auto ipv6 = TRY(decoder.decode<Array<u8, 16>>());
    return IPv6Address(ipv6);
}

template<>
ErrorOr<URL::URL> decode(Decoder& decoder)
{
    auto url_string = TRY(decoder.decode<ByteString>());
    auto url = URL::Parser::basic_parse(url_string);
    if (!url.has_value())
        return Error::from_string_view("Failed to parse URL in IPC Decode"sv);

    bool has_blob_url = TRY(decoder.decode<bool>());
    if (!has_blob_url)
        return url.release_value();

    url->set_blob_url_entry(URL::BlobURLEntry {
        .object = TRY(decoder.decode<URL::BlobURLEntry::Object>()),
        .environment { .origin = TRY(decoder.decode<URL::Origin>()) },
    });

    return url.release_value();
}

template<>
ErrorOr<URL::Origin> decode(Decoder& decoder)
{
    auto is_opaque = TRY(decoder.decode<bool>());
    if (is_opaque) {
        auto nonce = TRY(decoder.decode<URL::Origin::OpaqueData::Nonce>());
        auto type = TRY(decoder.decode<URL::Origin::OpaqueData::Type>());
        return URL::Origin { URL::Origin::OpaqueData { nonce, type } };
    }

    auto scheme = TRY(decoder.decode<Optional<String>>());
    auto host = TRY(decoder.decode<URL::Host>());
    auto port = TRY(decoder.decode<Optional<u16>>());
    auto domain = TRY(decoder.decode<Optional<String>>());

    return URL::Origin { move(scheme), move(host), port, move(domain) };
}

template<>
ErrorOr<URL::Host> decode(Decoder& decoder)
{
    auto value = TRY(decoder.decode<URL::Host::VariantType>());
    return URL::Host { move(value) };
}

template<>
ErrorOr<Empty> decode(Decoder&)
{
    return Empty {};
}

template<>
ErrorOr<Core::AnonymousBuffer> decode(Decoder& decoder)
{
    if (auto valid = TRY(decoder.decode<bool>()); !valid)
        return Core::AnonymousBuffer {};

    // NOTE: We don't use decode_size() here since AnonymousBuffer is backed by
    // shared memory, not heap allocation. The MAX_DECODED_SIZE limit doesn't
    // apply because the memory is already allocated by the sender.
    auto size = static_cast<size_t>(TRY(decoder.decode<u32>()));
    auto anon_file = TRY(decoder.decode<IPC::File>());

    return Core::AnonymousBuffer::create_from_anon_fd(anon_file.take_fd(), size);
}

template<>
ErrorOr<Core::ProxyData> decode(Decoder& decoder)
{
    auto type = TRY(decoder.decode<Core::ProxyData::Type>());
    auto host_ipv4 = IPv4Address(TRY(decoder.decode<u32>()));
    auto port = TRY(decoder.decode<u16>());

    return Core::ProxyData { type, host_ipv4, port };
}

template<>
ErrorOr<URL::BlobURLEntry::Blob> decode<URL::BlobURLEntry::Blob>(Decoder& decoder)
{
    return URL::BlobURLEntry::Blob {
        .type = TRY(decoder.decode<String>()),
        .data = TRY(decoder.decode<ByteBuffer>())
    };
}

template<>
ErrorOr<URL::BlobURLEntry::MediaSource> decode<URL::BlobURLEntry::MediaSource>(Decoder&)
{
    return URL::BlobURLEntry::MediaSource {};
}

}
