/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.apache.causeway.testdomain.util.dto;

import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.stream.Stream;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlRootElement;

import org.apache.causeway.applib.annotation.Programmatic;
import org.apache.causeway.commons.internal.base._Bytes;
import org.apache.causeway.commons.internal.base._Strings;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@XmlRootElement(name = "book")
@XmlAccessorType(XmlAccessType.PROPERTY)
@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class BookDto {

    private String name;
    private String description;
    private double price;

    private String author;
    private String isbn;
    private String publisher;

    public static BookDto from(final IBook book) {
        return BookDto.builder()
        .author(book.getAuthor())
        .description(book.getDescription())
        .isbn(book.getIsbn())
        .name(book.getName())
        .price(book.getPrice())
        .publisher(book.getPublisher())
        .build();
    }

    @Programmatic
    public BookDtoBuilder asBuilder() {
        return BookDto.builder()
        .author(this.getAuthor())
        .description(this.getDescription())
        .isbn(this.getIsbn())
        .name(this.getName())
        .price(this.getPrice())
        .publisher(this.getPublisher());
    }

    public static Stream<BookDto> samples() {

        return Stream.of(
                BookDto.builder()
                .author("Frank Herbert")
                .description("A sample book for testing. (1)")
                .isbn("ISBN-A")
                .name("Dune")
                .price(39.)
                .publisher("Sample Publisher (1)")
                .build(),
                BookDto.builder()
                .author("Isaac Asimov")
                .description("A sample book for testing. (2)")
                .isbn("ISBN-B")
                .name("The Foundation")
                .price(29.)
                .publisher("Sample Publisher (2)")
                .build(),
                BookDto.builder()
                .author("Herbert George Wells")
                .description("A sample book for testing. (3)")
                .isbn("ISBN-C")
                .name("The Time Machine")
                .price(99.)
                .publisher("Sample Publisher (3)")
                .build()
                );
    }

    public static BookDto sample() {
        return samples().findFirst().orElseThrow();
    }

    @Programmatic
    public String encode() throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance(BookDto.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        StringWriter sw = new StringWriter();
        jaxbMarshaller.marshal(this, sw);
        String newBookXml = sw.toString();

        String encoded = _Strings.convert(newBookXml, _Bytes.asCompressedUrlBase64, StandardCharsets.UTF_8);

        return encoded;
    }

    public static BookDto decode(final String encoded) throws JAXBException {
        String bookXml = _Strings.convert(encoded, _Bytes.ofCompressedUrlBase64, StandardCharsets.UTF_8);

        JAXBContext jaxbContext = JAXBContext.newInstance(BookDto.class);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        BookDto bookDto = (BookDto) jaxbUnmarshaller.unmarshal(new StringReader(bookXml));

        return bookDto;
    }

}
