package com.shuimin.pond.codec.restful;

import com.shuimin.common.S;
import com.shuimin.common.sql.Sql;
import com.shuimin.common.sql.SqlSelect;
import com.shuimin.pond.codec.Page;
import com.shuimin.pond.core.Request;
import com.shuimin.pond.core.Response;
import com.shuimin.pond.core.http.HttpMethod;
import com.shuimin.pond.core.mw.Controller;
import com.shuimin.pond.db.DB;
import com.shuimin.pond.db.Record;
import com.shuimin.pond.db.RecordService;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.shuimin.common.S._for;
import static com.shuimin.pond.core.Pond.debug;
import static com.shuimin.pond.core.Render.*;

/**
 * Created by ed on 14-5-20.
 * <p/>
 * TODO :unit test
 */
public class RestfulController<E extends Record> extends Controller {

    protected E proto;
    protected RecordService<E> service;

    protected String SORD = "_sord";
    protected String SORDF = "_sordf";

    public RestfulController(E e) {
        this.proto = e;
        service = RecordService.build(e);
    }

    public SqlSelect sqlFromReq(Request req) {
        String tb_name = proto.table();
        Set<String> fields = proto.declaredFields();
        SqlSelect sql = Sql.select(fields.toArray(new String[fields.size()])).from(tb_name)
                .where(req.toQuery(proto.declaredFields()));
        String sord = req.param(SORD);
        String sord_f = req.param(SORDF);
        if (S.str.notBlank(sord)
                && S.str.notBlank(sord_f)
                ) {
            String order;
            if (fields.contains(sord_f)) {
                order = sord_f;
            } else
                throw new RuntimeException("sordf not valid");
            if (sord.equalsIgnoreCase("desc")) {
                sql.orderBy(order + " desc");
            } else {
                sql.orderBy(order + " asc");
            }
        }
        return sql;
    }

    public Page queryForPage(Request req) {
        return DB.fire(tmpl -> {
            Page page = Page.of(req);
            SqlSelect select = sqlFromReq(req);
            if (Page.allowPage(req))
                select.offset(Page.getOffset(req))
                        .limit(Page.getLimit(req));
            List<E> data =
                    tmpl.map(proto.mapper()::map, select.tuple());
            int count = tmpl.count(select.count().tuple());
            List<Map<String, Object>> view =
                    _for(data).map(Record::view).toList();
            return page.fulfill(view, count);
        });
    }

    @Mapping(value = "/", methods = {HttpMethod.GET})
    public void index(Request req, Response res) {
        String mime = getAcceptHeader(req).trim().toLowerCase();
        if (mime.startsWith("text/html"))
            res.render(view(resourcePath("index.view"), queryForPage(req)));
        else
            res.render(json(queryForPage(req)));

    }

    @Mapping(value = "/", methods = {HttpMethod.POST})
    public void create(Request req, Response res) {
        res.render(json(service.create(req.toMap())));
    }

    @Mapping(value = "/new", methods = {HttpMethod.GET})
    public void view_new(Request req, Response res) {
        res.render(view(resourcePath("new.view")));
    }

    @Mapping(value = "/${_id}/edit", methods = {HttpMethod.GET})
    public void view_edit(Request req, Response res) {
        String id = req.param("_id");
        Record o = service.get(id);
        res.render(view(resourcePath("edit.view"), o.view()));
    }

    @Mapping(value = "/${_id}", methods = {HttpMethod.GET})
    public void get(Request req, Response res) {
        String id = req.param("_id");
        String mime = getAcceptHeader(req).trim().toLowerCase();
        if (mime.startsWith("text/html")) {
            Object render = service.get(id);
            if (render != null)
                res.render(view(resourcePath("detail.view"), render));
            else
                res.send(404, id + " not found.");
        } else
            res.render(json(service.get(id)));
    }

    @Mapping(value = "/${_id}", methods = {HttpMethod.PUT,HttpMethod.POST})
    public void update(Request req, Response res) {
        String id = req.param("_id");
        res.render(json(service.update(id, req.toMap())));
    }

    @Mapping(value = "/${_id}", methods = {HttpMethod.DELETE})
    public void delete(Request req, Response res) {
        String id = req.param("_id");
        res.render(text(service.delete(id)));
    }


    static String getAcceptHeader(Request req) {
        String accept = _for(req.header("Accept")).first();
        debug("Accept:" + accept);
        return S.str.notBlank(accept) ? accept : "text/html";
    }

    String resourcePath(String name) {
        return File.separator + prefix + File.separator + name;
    }

}

@@ -3781,22 +3781,22 @@
         
-Object
+Record
  render 
@@ -3913,16 +3913,23 @@
 , render
+.view()
 ));%0A    

fix view