module transaction.api;

import handy_http_primitives;
import handy_http_data.json;
import handy_http_handlers.path_handler;
import slf4d;
import std.typecons;

import transaction.model;
import transaction.data;
import transaction.service;
import transaction.dto;
import profile.data;
import profile.service;
import account.api;
import util.money;
import util.pagination;
import util.data;

// Transactions API

immutable DEFAULT_TRANSACTION_PAGE = PageRequest(1, 10, [Sort("txn.timestamp", SortDir.DESC)]);

void handleGetTransactions(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    PageRequest pr = PageRequest.parse(request, DEFAULT_TRANSACTION_PAGE);
    auto responsePage = getTransactions(ds, pr);
    writeJsonBody(response, responsePage);
}

void handleSearchTransactions(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    PageRequest pr = PageRequest.parse(request, DEFAULT_TRANSACTION_PAGE);
    auto page = ds.getTransactionRepository().search(pr, request);
    writeJsonBody(response, page);
}

void handleGetTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    TransactionDetail txn = getTransaction(ds, getTransactionIdOrThrow(request));
    import asdf : serializeToJson;
    string jsonStr = serializeToJson(txn);
    response.writeBodyString(jsonStr, "application/json");
}

void handleAddTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    import asdf : serializeToJson;
    auto fullPayload = parseMultipartFilesAndBody!AddTransactionPayload(request);
    ProfileDataSource ds = getProfileDataSource(request);
    TransactionDetail txn = addTransaction(ds, fullPayload.payload, fullPayload.files);
    string jsonStr = serializeToJson(txn);
    response.writeBodyString(jsonStr, "application/json");
}

void handleUpdateTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    import asdf : serializeToJson;
    ProfileDataSource ds = getProfileDataSource(request);
    ulong txnId = getTransactionIdOrThrow(request);
    auto fullPayload = parseMultipartFilesAndBody!AddTransactionPayload(request);
    TransactionDetail txn = updateTransaction(ds, txnId, fullPayload.payload, fullPayload.files);
    string jsonStr = serializeToJson(txn);
    response.writeBodyString(jsonStr, "application/json");
}

void handleDeleteTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    ulong txnId = getTransactionIdOrThrow(request);
    deleteTransaction(ds, txnId);
}

void handleGetAllTags(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    string[] tags = ds.getTransactionTagRepository().findAll();
    writeJsonBody(response, tags);
}

private ulong getTransactionIdOrThrow(in ServerHttpRequest request) {
    return getPathParamOrThrow!ulong(request, "transactionId");
}

// Vendors API

void handleGetVendors(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    TransactionVendor[] vendors = getAllVendors(ds);
    writeJsonBody(response, vendors);
}

void handleGetVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    TransactionVendor vendor = getVendor(ds, getVendorId(request));
    writeJsonBody(response, vendor);
}

struct VendorPayload {
    string name;
    string description;
}

void handleCreateVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    VendorPayload payload = readJsonBodyAs!VendorPayload(request);
    ProfileDataSource ds = getProfileDataSource(request);
    TransactionVendor vendor = createVendor(ds, payload);
    writeJsonBody(response, vendor);
}

void handleUpdateVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    VendorPayload payload = readJsonBodyAs!VendorPayload(request);
    ProfileDataSource ds = getProfileDataSource(request);
    TransactionVendor updated = updateVendor(ds, getVendorId(request), payload);
    writeJsonBody(response, updated);
}

void handleDeleteVendor(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    deleteVendor(ds, getVendorId(request));
}

private ulong getVendorId(in ServerHttpRequest request) {
    return getPathParamOrThrow!ulong(request, "vendorId");
}

// Categories API

void handleGetCategories(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    TransactionCategoryTree[] categories = getCategories(getProfileDataSource(request));
    writeJsonBody(response, categories);
}

void handleGetCategory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    auto category = getCategory(getProfileDataSource(request), getCategoryId(request));
    writeJsonBody(response, category);
}

struct CategoryPayload {
    string name;
    string description;
    string color;
    Nullable!ulong parentId;
}

void handleCreateCategory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    CategoryPayload payload = readJsonBodyAs!CategoryPayload(request);
    ProfileDataSource ds = getProfileDataSource(request);
    auto category = createCategory(ds, payload);
    writeJsonBody(response, category);
}

void handleUpdateCategory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    CategoryPayload payload = readJsonBodyAs!CategoryPayload(request);
    ProfileDataSource ds = getProfileDataSource(request);
    ulong categoryId = getCategoryId(request);
    auto category = updateCategory(ds, categoryId, payload);
    writeJsonBody(response, category);
}

void handleDeleteCategory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
    ProfileDataSource ds = getProfileDataSource(request);
    ulong categoryId = getCategoryId(request);
    deleteCategory(ds, categoryId);
}

private ulong getCategoryId(in ServerHttpRequest request) {
    return getPathParamOrThrow!ulong(request, "categoryId");
}
