// Copyright 2022 Cisco Systems, Inc. and its affiliates
//
// Licensed 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.
//
// SPDX-License-Identifier: Apache-2.0

/*
 * Flame REST API
 *
 * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
 *
 * API version: 1.0.0
 * Generated by: OpenAPI Generator (https://openapi-generator.tech)
 */

package openapi

import (
	"bytes"
	"net/http"
	"strings"
	"time"

	"github.com/gorilla/mux"

	"github.com/cisco-open/flame/pkg/openapi/constants"
)

// DesignCodeApiController binds http requests to an api service and writes the service results to the http response
type DesignCodeApiController struct {
	service      DesignCodeApiServicer
	errorHandler ErrorHandler
}

// DesignCodeApiOption for how the controller is set up.
type DesignCodeApiOption func(*DesignCodeApiController)

// WithDesignCodeApiErrorHandler inject ErrorHandler into controller
func WithDesignCodeApiErrorHandler(h ErrorHandler) DesignCodeApiOption {
	return func(c *DesignCodeApiController) {
		c.errorHandler = h
	}
}

// NewDesignCodeApiController creates a default api controller
func NewDesignCodeApiController(s DesignCodeApiServicer, opts ...DesignCodeApiOption) Router {
	controller := &DesignCodeApiController{
		service:      s,
		errorHandler: DefaultErrorHandler,
	}

	for _, opt := range opts {
		opt(controller)
	}

	return controller
}

// Routes returns all the api routes for the DesignCodeApiController
func (c *DesignCodeApiController) Routes() Routes {
	return Routes{
		{
			"CreateDesignCode",
			strings.ToUpper("Post"),
			"/users/{user}/designs/{designId}/code",
			c.CreateDesignCode,
		},
		{
			"DeleteDesignCode",
			strings.ToUpper("Delete"),
			"/users/{user}/designs/{designId}/code",
			c.DeleteDesignCode,
		},
		{
			"GetDesignCode",
			strings.ToUpper("Get"),
			"/users/{user}/designs/{designId}/code",
			c.GetDesignCode,
		},
		{
			"GetDesignCodeRevision",
			strings.ToUpper("Get"),
			"/users/{user}/designs/{designId}/code/revision",
			c.GetDesignCodeRevision,
		},
		{
			"UpdateDesignCode",
			strings.ToUpper("Put"),
			"/users/{user}/designs/{designId}/code",
			c.UpdateDesignCode,
		},
	}
}

// CreateDesignCode - Upload a new design code
//
//nolint:dupl
func (c *DesignCodeApiController) CreateDesignCode(w http.ResponseWriter, r *http.Request) {
	var maxMemory int64 = 32 << 20 // 32MB
	if err := r.ParseMultipartForm(maxMemory); err != nil {
		c.errorHandler(w, r, &ParsingError{Err: err}, nil)
		return
	}
	params := mux.Vars(r)
	userParam := params[constants.ParamUser]

	designIdParam := params[constants.ParamDesignID]

	fileNameParam := r.FormValue("fileName")

	fileDataParam, err := ReadFormFileToTempFile(r, "fileData")
	if err != nil {
		c.errorHandler(w, r, &ParsingError{Err: err}, nil)
		return
	}
	result, err := c.service.CreateDesignCode(r.Context(), userParam, designIdParam, fileNameParam, fileDataParam)
	// If an error occurred, encode the error with the status code
	if err != nil {
		c.errorHandler(w, r, err, &result)
		return
	}
	// If no error, encode the body and the result code
	EncodeJSONResponse(result.Body, &result.Code, w)
}

// DeleteDesignCode - Delete code of a given design
func (c *DesignCodeApiController) DeleteDesignCode(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	userParam := params[constants.ParamUser]

	designIdParam := params[constants.ParamDesignID]

	result, err := c.service.DeleteDesignCode(r.Context(), userParam, designIdParam)
	// If an error occurred, encode the error with the status code
	if err != nil {
		c.errorHandler(w, r, err, &result)
		return
	}
	// If no error, encode the body and the result code
	EncodeJSONResponse(result.Body, &result.Code, w)
}

// GetDesignCode - Get a zipped design code file owned by user
func (c *DesignCodeApiController) GetDesignCode(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	userParam := params[constants.ParamUser]

	designIdParam := params[constants.ParamDesignID]

	result, err := c.service.GetDesignCode(r.Context(), userParam, designIdParam)
	// If an error occurred, encode the error with the status code
	if err != nil {
		c.errorHandler(w, r, err, &result)
		return
	}

	// Note by Myungjin: EncodeJSONResponse can't handle binary file transfer
	//                   Use ServeContent instead.
	http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(result.Body.([]byte)))
}

// GetDesignCodeRevision - Get a revision number of design code
func (c *DesignCodeApiController) GetDesignCodeRevision(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	userParam := params["user"]
	designIdParam := params["designId"]
	result, err := c.service.GetDesignCodeRevision(r.Context(), userParam, designIdParam)
	// If an error occurred, encode the error with the status code
	if err != nil {
		c.errorHandler(w, r, err, &result)
		return
	}
	// If no error, encode the body and the result code
	EncodeJSONResponse(result.Body, &result.Code, w)
}

// UpdateDesignCode - Update a design code
//
//nolint:dupl
func (c *DesignCodeApiController) UpdateDesignCode(w http.ResponseWriter, r *http.Request) {
	var maxMemory int64 = 32 << 20 // 32MB
	if err := r.ParseMultipartForm(maxMemory); err != nil {
		c.errorHandler(w, r, &ParsingError{Err: err}, nil)
		return
	}
	params := mux.Vars(r)
	userParam := params[constants.ParamUser]

	designIdParam := params[constants.ParamDesignID]

	fileNameParam := r.FormValue("fileName")

	fileDataParam, err := ReadFormFileToTempFile(r, "fileData")
	if err != nil {
		c.errorHandler(w, r, &ParsingError{Err: err}, nil)
		return
	}
	result, err := c.service.UpdateDesignCode(r.Context(), userParam, designIdParam, fileNameParam, fileDataParam)
	// If an error occurred, encode the error with the status code
	if err != nil {
		c.errorHandler(w, r, err, &result)
		return
	}
	// If no error, encode the body and the result code
	EncodeJSONResponse(result.Body, &result.Code, w)
}
