package v1

import (
	"fmt"

	"github.com/go-chi/chi/v5"
	"github.com/porter-dev/porter/api/server/handlers/registry"
	v1Registry "github.com/porter-dev/porter/api/server/handlers/v1/registry"
	"github.com/porter-dev/porter/api/server/shared"
	"github.com/porter-dev/porter/api/server/shared/config"
	"github.com/porter-dev/porter/api/server/shared/router"
	"github.com/porter-dev/porter/api/types"
)

// swagger:parameters getRegistry deleteRegistry createRegistryRepository listRegistryRepositories listRegistryImages
type registryPathParams struct {
	// The project id
	// in: path
	// required: true
	// minimum: 1
	ProjectID uint `json:"project_id"`

	// The registry id
	// in: path
	// required: true
	// minimum: 1
	RegistryID uint `json:"registry_id"`
}

func NewV1RegistryScopedRegisterer(children ...*router.Registerer) *router.Registerer {
	return &router.Registerer{
		GetRoutes: GetV1RegistryScopedRoutes,
		Children:  children,
	}
}

func GetV1RegistryScopedRoutes(
	r chi.Router,
	config *config.Config,
	basePath *types.Path,
	factory shared.APIEndpointFactory,
	children ...*router.Registerer,
) []*router.Route {
	routes, projPath := getV1RegistryRoutes(r, config, basePath, factory)

	if len(children) > 0 {
		r.Route(projPath.RelativePath, func(r chi.Router) {
			for _, child := range children {
				childRoutes := child.GetRoutes(r, config, basePath, factory, child.Children...)

				routes = append(routes, childRoutes...)
			}
		})
	}

	return routes
}

func getV1RegistryRoutes(
	r chi.Router,
	config *config.Config,
	basePath *types.Path,
	factory shared.APIEndpointFactory,
) ([]*router.Route, *types.Path) {
	relPath := "/registries"

	newPath := &types.Path{
		Parent:       basePath,
		RelativePath: relPath,
	}

	var routes []*router.Route

	// POST /api/v1/projects/{project_id}/registries -> registry.NewRegistryCreateHandler
	// swagger:operation POST /api/v1/projects/{project_id}/registries createRegistry
	//
	// Connects a new image registry to the project denoted by `project_id`.
	//
	// ---
	// produces:
	// - application/json
	// summary: Connect an image registry
	// tags:
	// - Registries
	// parameters:
	//   - name: project_id
	//   - in: body
	//     name: CreateRegistryRequest
	//     description: The registry to connect
	//     schema:
	//       $ref: '#/definitions/CreateRegistryRequest'
	// responses:
	//   '201':
	//     description: Successfully connected the registry
	//     schema:
	//       $ref: '#/definitions/CreateRegistryResponse'
	//   '400':
	//     description: A malformed or bad request
	//   '403':
	//     description: Forbidden
	//   '404':
	//     description: A subresource was not found
	createRegistryEndpoint := factory.NewAPIEndpoint(
		&types.APIRequestMetadata{
			Verb:   types.APIVerbCreate,
			Method: types.HTTPVerbPost,
			Path: &types.Path{
				Parent:       basePath,
				RelativePath: relPath,
			},
			Scopes: []types.PermissionScope{
				types.UserScope,
				types.ProjectScope,
			},
		},
	)

	createRegistryHandler := registry.NewRegistryCreateHandler(
		config,
		factory.GetDecoderValidator(),
		factory.GetResultWriter(),
	)

	routes = append(routes, &router.Route{
		Endpoint: createRegistryEndpoint,
		Handler:  createRegistryHandler,
		Router:   r,
	})

	// GET /api/v1/projects/{project_id}/registries/{registry_id} -> registry.NewRegistryGetHandler
	// swagger:operation GET /api/v1/projects/{project_id}/registries/{registry_id} getRegistry
	//
	// Gets an image registry denoted by `registry_id`. The registry should belong to the
	// project denoted by `project_id`.
	//
	// ---
	// produces:
	// - application/json
	// summary: Get an image registry
	// tags:
	// - Registries
	// parameters:
	//   - name: project_id
	//   - name: registry_id
	// responses:
	//   '200':
	//     description: Successfully got the registry
	//     schema:
	//       $ref: '#/definitions/GetRegistryResponse'
	//   '403':
	//     description: Forbidden
	getEndpoint := factory.NewAPIEndpoint(
		&types.APIRequestMetadata{
			Verb:   types.APIVerbGet,
			Method: types.HTTPVerbGet,
			Path: &types.Path{
				Parent:       basePath,
				RelativePath: relPath + "/{registry_id}",
			},
			Scopes: []types.PermissionScope{
				types.UserScope,
				types.ProjectScope,
				types.RegistryScope,
			},
		},
	)

	getHandler := registry.NewRegistryGetHandler(
		config,
		factory.GetResultWriter(),
	)

	routes = append(routes, &router.Route{
		Endpoint: getEndpoint,
		Handler:  getHandler,
		Router:   r,
	})

	// GET /api/v1/projects/{project_id}/registries -> registry.NewRegistryListHandler
	// swagger:operation GET /api/v1/projects/{project_id}/registries listRegistries
	//
	// Lists all registries connected to the project denoted by `project_id`.
	//
	// ---
	// produces:
	// - application/json
	// summary: List image registries
	// tags:
	// - Registries
	// parameters:
	//   - name: project_id
	//   - name: registry_id
	// responses:
	//   '200':
	//     description: Successfully listed registries
	//     schema:
	//       $ref: '#/definitions/ListRegistriesResponse'
	//   '403':
	//     description: Forbidden
	listRegistriesEndpoint := factory.NewAPIEndpoint(
		&types.APIRequestMetadata{
			Verb:   types.APIVerbList,
			Method: types.HTTPVerbGet,
			Path: &types.Path{
				Parent:       basePath,
				RelativePath: relPath,
			},
			Scopes: []types.PermissionScope{
				types.UserScope,
				types.ProjectScope,
			},
		},
	)

	listRegistriesHandler := registry.NewRegistryListHandler(
		config,
		factory.GetResultWriter(),
	)

	routes = append(routes, &router.Route{
		Endpoint: listRegistriesEndpoint,
		Handler:  listRegistriesHandler,
		Router:   r,
	})

	// DELETE /api/v1/projects/{project_id}/registries/{registry_id} -> registry.NewRegistryDeleteHandler
	// swagger:operation DELETE /api/v1/projects/{project_id}/registries/{registry_id} deleteRegistry
	//
	// Deletes a registry denoted by `registry_id`. The registry should belong to
	// the project denoted by `project_id`.
	//
	// ---
	// produces:
	// - application/json
	// summary: Disconnect image registry
	// tags:
	// - Registries
	// parameters:
	//   - name: project_id
	//   - name: registry_id
	// responses:
	//   '200':
	//     description: Successfully disconnected image registry
	//   '403':
	//     description: Forbidden
	deleteEndpoint := factory.NewAPIEndpoint(
		&types.APIRequestMetadata{
			Verb:   types.APIVerbDelete,
			Method: types.HTTPVerbDelete,
			Path: &types.Path{
				Parent:       basePath,
				RelativePath: relPath + "/{registry_id}",
			},
			Scopes: []types.PermissionScope{
				types.UserScope,
				types.ProjectScope,
				types.RegistryScope,
			},
		},
	)

	deleteHandler := registry.NewRegistryDeleteHandler(
		config,
		factory.GetResultWriter(),
	)

	routes = append(routes, &router.Route{
		Endpoint: deleteEndpoint,
		Handler:  deleteHandler,
		Router:   r,
	})

	// POST /api/v1/projects/{project_id}/registries/{registry_id}/repositories -> registry.NewRegistryCreateRepositoryHandler
	// swagger:operation POST /api/v1/projects/{project_id}/registries/{registry_id}/repositories createRegistryRepository
	//
	// Creates an image repository inside the registry specified by `registry_id`. This method **only** creates repositories for ECR-integrated
	// repositories.
	//
	// ---
	// produces:
	// - application/json
	// summary: Create image repository
	// tags:
	// - Registries
	// parameters:
	//   - name: project_id
	//   - name: registry_id
	//   - in: body
	//     name: CreateRepositoryRequest
	//     description: The repository to create
	//     schema:
	//       $ref: '#/definitions/CreateRegistryRepositoryRequest'
	// responses:
	//   '201':
	//     description: Successfully created the image repository
	//   '403':
	//     description: Forbidden
	createRepositoryEndpoint := factory.NewAPIEndpoint(
		&types.APIRequestMetadata{
			Verb:   types.APIVerbCreate,
			Method: types.HTTPVerbPost,
			Path: &types.Path{
				Parent:       basePath,
				RelativePath: relPath + "/{registry_id}/repositories",
			},
			Scopes: []types.PermissionScope{
				types.UserScope,
				types.ProjectScope,
				types.RegistryScope,
			},
		},
	)

	createRepositoryHandler := registry.NewRegistryCreateRepositoryHandler(
		config,
		factory.GetDecoderValidator(),
		factory.GetResultWriter(),
	)

	routes = append(routes, &router.Route{
		Endpoint: createRepositoryEndpoint,
		Handler:  createRepositoryHandler,
		Router:   r,
	})

	// GET /api/v1/projects/{project_id}/registries/{registry_id}/repositories -> registry.NewRegistryListRepositoriesHandler
	// swagger:operation GET /api/v1/projects/{project_id}/registries/{registry_id}/repositories listRegistryRepositories
	//
	// Lists image repositories inside the image registry denoted by `registry_id`. The registry
	// should belong to the project denoted by `project_id`.
	//
	// ---
	// produces:
	// - application/json
	// summary: List image repositories
	// tags:
	// - Registries
	// parameters:
	//   - name: project_id
	//   - name: registry_id
	// responses:
	//   '200':
	//     description: Successfully listed image repositories
	//     schema:
	//       $ref: '#/definitions/ListRegistryRepositoriesResponse'
	//   '403':
	//     description: Forbidden
	listRepositoriesEndpoint := factory.NewAPIEndpoint(
		&types.APIRequestMetadata{
			Verb:   types.APIVerbList,
			Method: types.HTTPVerbGet,
			Path: &types.Path{
				Parent:       basePath,
				RelativePath: relPath + "/{registry_id}/repositories",
			},
			Scopes: []types.PermissionScope{
				types.UserScope,
				types.ProjectScope,
				types.RegistryScope,
			},
		},
	)

	listRepositoriesHandler := registry.NewRegistryListRepositoriesHandler(
		config,
		factory.GetResultWriter(),
	)

	routes = append(routes, &router.Route{
		Endpoint: listRepositoriesEndpoint,
		Handler:  listRepositoriesHandler,
		Router:   r,
	})

	// GET /api/v1/projects/{project_id}/registries/{registry_id}/repositories/* -> registry.NewRegistryListImagesHandler
	// swagger:operation GET /api/v1/projects/{project_id}/registries/{registry_id}/repositories/{repository} listRegistryImages
	//
	// Lists all images in the image repository denoted by the name `repository`. The repository should belong
	// to the registry denoted by `registry_id` which should itself belong to the project denoted by
	// `project_id`.
	//
	// ---
	// produces:
	// - application/json
	// summary: List images
	// tags:
	// - Registries
	// parameters:
	//   - name: project_id
	//   - name: registry_id
	//   - name: repository
	//     in: path
	//     description: The image repository name. Should be of the form REPOSITORY/IMAGE when using Google Artifact Registry.
	//     type: string
	//     required: true
	//   - name: num
	//     in: query
	//     description: |
	//       The number of images to list.
	//       For ECR images, a maximum of 1000 is allowed.
	//     type: integer
	//     required: false
	//     minimum: 1
	//   - name: next
	//     in: query
	//     description: The next page string used for pagination, from a previous request.
	//     type: string
	//   - name: page
	//     in: query
	//     description: |
	//       The page number used for pagination, possibly from a previous request.
	//       (**DigitalOcean only**)
	//     type: integer
	//     minimum: 1
	// responses:
	//   '200':
	//     description: Successfully listed images
	//     schema:
	//       $ref: '#/definitions/V1ListImageResponse'
	//   '400':
	//     description: A malformed or bad request
	//   '403':
	//     description: Forbidden
	listImagesEndpoint := factory.NewAPIEndpoint(
		&types.APIRequestMetadata{
			Verb:   types.APIVerbList,
			Method: types.HTTPVerbGet,
			Path: &types.Path{
				Parent: basePath,
				RelativePath: fmt.Sprintf(
					"%s/{registry_id}/repositories/%s",
					relPath,
					types.URLParamWildcard,
				),
			},
			Scopes: []types.PermissionScope{
				types.UserScope,
				types.ProjectScope,
				types.RegistryScope,
			},
		},
	)

	listImagesHandler := v1Registry.NewRegistryListImagesHandler(
		config,
		factory.GetDecoderValidator(),
		factory.GetResultWriter(),
	)

	routes = append(routes, &router.Route{
		Endpoint: listImagesEndpoint,
		Handler:  listImagesHandler,
		Router:   r,
	})

	return routes, newPath
}
