// Package server implements the server.
package server

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"strings"

	"git.lyda.ie/kevin/boxes/api"
	"git.lyda.ie/kevin/boxes/database/store"
)

func tags2db(tags *[]string) sql.NullString {
	if tags == nil {
		return sql.NullString{}
	}
	return sql.NullString{
		String: fmt.Sprintf(",%s,", strings.Join(*tags, ",")),
		Valid:  true,
	}
}

func db2tags(tags sql.NullString) *[]string {
	if tags.Valid {
		t := strings.Split(strings.Trim(tags.String, ","), ",")
		return &t
	}
	return nil
}

// GetContent TODO.
func (ep *Endpoints) GetContent(ctx context.Context, req api.GetContentRequestObject) (api.GetContentResponseObject, error) {
	content, err := ep.db.GetContent(ctx, req.Cid)
	if err != nil {
		log.Printf("error: %+v", err)
		return api.GetContent404JSONResponse{
			Code:    404,
			Message: "Not found",
		}, nil
	}
	return api.GetContent200JSONResponse{
		ContentJSONResponse: api.ContentJSONResponse{
			Id:          content.ID,
			Box:         content.Box,
			Description: content.Description,
			Tags:        db2tags(content.Tags),
		}}, nil
}

// CreateContent TODO.
func (ep *Endpoints) CreateContent(ctx context.Context, req api.CreateContentRequestObject) (api.CreateContentResponseObject, error) {
	if req.Cid != req.Body.Id {
		log.Printf("error: %+v != %+v", req.Cid, req.Body)
		return api.CreateContent406JSONResponse{
			Code:    406,
			Message: "Path id not equal to sent id",
		}, nil
	}
	newContent := store.CreateContentParams{
		ID:          req.Cid,
		Box:         req.Body.Box,
		Description: req.Body.Description,
		Tags:        tags2db(req.Body.Tags),
	}
	result, err := ep.db.CreateContent(ctx, newContent)
	if err != nil {
		log.Printf("error: %+v", err)
		return api.CreateContent507JSONResponse{
			Code:    507,
			Message: "Failed to store",
		}, nil
	}
	return api.CreateContent200JSONResponse{
		ContentJSONResponse: api.ContentJSONResponse{
			Id:          result.ID,
			Box:         result.Box,
			Description: result.Description,
			Tags:        db2tags(result.Tags),
		}}, nil
}

// UpdateContent TODO.
func (ep *Endpoints) UpdateContent(ctx context.Context, req api.UpdateContentRequestObject) (api.UpdateContentResponseObject, error) {
	if req.Cid != req.Body.Id {
		return api.UpdateContent406JSONResponse{
			Code:    406,
			Message: "Path id not equal to sent id",
		}, nil
	}
	newContent := store.UpdateContentParams{
		ID:          req.Cid,
		Box:         req.Body.Box,
		Description: req.Body.Description,
		Tags:        tags2db(req.Body.Tags),
	}
	result, err := ep.db.UpdateContent(ctx, newContent)
	if err != nil {
		log.Printf("error: %+v", err)
		return api.UpdateContent507JSONResponse{
			Code:    507,
			Message: "Failed to store",
		}, nil
	}
	return api.UpdateContent200JSONResponse{
		ContentJSONResponse: api.ContentJSONResponse{
			Id:          result.ID,
			Box:         result.Box,
			Description: result.Description,
			Tags:        db2tags(result.Tags),
		}}, nil
}

// DeleteContent TODO.
func (ep *Endpoints) DeleteContent(ctx context.Context, req api.DeleteContentRequestObject) (api.DeleteContentResponseObject, error) {
	result, err := ep.db.DeleteContent(ctx, req.Cid)
	if err != nil {
		log.Printf("error: %+v", err)
		return api.DeleteContent404JSONResponse{
			Code:    404,
			Message: "Failed to delete",
		}, nil
	}
	return api.DeleteContent200JSONResponse{
		ContentJSONResponse: api.ContentJSONResponse{
			Id:          result.ID,
			Box:         result.Box,
			Description: result.Description,
			Tags:        db2tags(result.Tags),
		}}, nil
}

// GetContents returns a list of contents.
func (ep *Endpoints) GetContents(ctx context.Context, req api.GetContentsRequestObject) (api.GetContentsResponseObject, error) {
	var contents []store.Content
	var err error
	if req.Params.Filter != nil {
		contents, err = ep.db.GetContentsWithFilter(ctx, fmt.Sprintf("%%%s%%", *req.Params.Filter))
	} else if req.Params.Box == nil {
		contents, err = ep.db.GetContentsWithBox(ctx, *req.Params.Box)
	} else if req.Params.Location == nil {
		contents, err = ep.db.GetContentsWithLocation(ctx, *req.Params.Location)
	} else if req.Params.Tags == nil {
		if len(*req.Params.Tags) == 1 {
			contents, err = ep.db.GetContentsWithTag(ctx, sql.NullString{
				String: fmt.Sprintf("%%,%s,%%", (*req.Params.Tags)[0]),
				Valid:  true,
			})
		} else {
			// TODO: figure out more than one tag.
			contents, err = ep.db.GetContents(ctx)
		}
	} else {
		contents, err = ep.db.GetContents(ctx)
	}
	if err != nil {
		log.Printf("error: %+v", err)
		return api.GetContents500JSONResponse{
			Code:    500,
			Message: "Failed to fetch",
		}, nil
	}
	result := make(api.Contents, len(contents))
	for i := range contents {
		result[i].Id = contents[i].ID
		result[i].Box = contents[i].Box
		result[i].Description = contents[i].Description
		result[i].Tags = db2tags(contents[i].Tags)
	}

	return api.GetContents200JSONResponse{
		ContentsJSONResponse: result,
	}, nil
}