package leigh.tansu.service;

import jakarta.servlet.http.Part;
import leigh.eo.EO;
import leigh.esp.sdk.service.MicroService;
import leigh.esp.sdk.service.schema.v1.MicroServiceCfgElementDAO;
import leigh.mecha.http.server.WebTransaction;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.util.UniversalJob;
import leigh.tansu.sdk.v1.TansuClient;
import leigh.tansu.sdk.v1.schema.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.CountingOutputStream;

import java.io.IOException;

/**
 * Tansu implements a simple object store which can be used to store a wide variety of binary objects (BLOB)s.
 *
 * @author Alex Leigh
 */
public class TansuService extends MicroService {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(TansuService.class);
    private final TansuFileStore store = new TansuFileStore();

    public TansuService(EO cfg) throws IOException, ClassNotFoundException {
        super(cfg);
    }

    public static void main(String args[]) throws IOException, ClassNotFoundException {
        UniversalJob.banner(logger, "Tansu Service mk1");
        EO cfg = MicroServiceCfgElementDAO.create();
        MicroServiceCfgElementDAO.setSrcUri(cfg, "/tansu/v1");
        MicroServiceCfgElementDAO.setKabeUrl(cfg, "http://localhost:8123");
        TansuService svc = new TansuService(cfg);
        svc.run();
    }

    @Override
    public void serviceRequest(WebTransaction request) throws Throwable {
        logger.info("Processing request. {}", request);

        try {
            switch (request.getPathInfo()) {
                case TansuClient.API_BUCKET_CREATE -> bucketCreate(request);
                case TansuClient.API_OBJECT_GET -> objectGet(request);
                case TansuClient.API_OBJECT_PUT -> objectPut(request);
                default -> {
                    logger.info("Could not find handler. [path: {}]", request.getPathInfo());
                    sendError(request, 404);
                }
            }

        } catch (Throwable t) {
            t.printStackTrace();
            throw t;
        }
    }

    /**
     * Perform an object put operation.
     * <p>
     * Note that we get the content-type here from the multi-part part, not from the request. Therefore it is
     * essential that the client has set that properly.
     */
    private void objectPut(WebTransaction txn) throws Exception {
        // It is strongly implied this is a multi-part message, but if it is not, we'll sure find out later.
        EO obj = getInputObject(txn);
        Part part = txn.httpServletRequest.getPart(TansuClient.MULTIPART_BLOB);
        store.write(ObjectPutRequestElementDAO.getBucketId(obj),
                ObjectPutRequestElementDAO.getId(obj),
                part.getInputStream(), part.getContentType());
        sendResponse(txn, ObjectPutResponseElementDAO.create());
    }

    private void objectGet(WebTransaction txn) throws Exception {
        EO obj = getInputObject(txn);
        // TODO: Error check for missing
        try (TansuFileObject blob = store.read(ObjectGetRequestElementDAO.getBucketId(obj),
                ObjectGetRequestElementDAO.getId(obj))) {
            EO meta = blob.readMeta();
            String contentType = meta.getValueString(TansuFileStore.META_CONTENT_TYPE_KEY);
            txn.httpServletResponse.setContentType(contentType);
            CountingOutputStream cos = new CountingOutputStream(txn.httpServletResponse.getOutputStream());
            IOUtils.copy(blob.getMainInputStream(), cos);
            logger.info("Write {} bytes.", cos.getByteCount());
            cos.close();
        }
    }

    private void bucketCreate(WebTransaction txn) throws Exception {
        EO obj = getInputObject(txn);
        String bucketId = BucketCreateRequestElementDAO.getId(obj);
        store.createBucket(bucketId);
        sendResponse(txn, BucketCreateResponseElementDAO.create());
    }
}
