// Copyright 2026 MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT

package repo

import (
	"net/http"

	repo_model "code.gitea.io/gitea/models/repo"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/modules/web"
	"code.gitea.io/gitea/services/context"
	pages_service "code.gitea.io/gitea/services/pages"
)

// GetPagesConfig returns the pages configuration for a repository
func GetPagesConfig(ctx *context.APIContext) {
	// swagger:operation GET /repos/{owner}/{repo}/pages repository repoGetPages
	// ---
	// summary: Get pages configuration for a repository
	// produces:
	// - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// responses:
	//   "200":
	//     "$ref": "#/responses/PagesInfo"
	//   "404":
	//     "$ref": "#/responses/notFound"

	config, err := pages_service.GetPagesConfig(ctx, ctx.Repo.Repository)
	if err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	domains, err := pages_service.GetPagesDomains(ctx, ctx.Repo.Repository.ID)
	if err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	ctx.JSON(http.StatusOK, &api.PagesInfo{
		Config: &api.PagesConfig{
			Enabled:   config != nil && config.Enabled,
			Template:  getTemplateOrDefault(config),
			Subdomain: pages_service.GetPagesSubdomain(ctx.Repo.Repository),
			URL:       pages_service.GetPagesURL(ctx.Repo.Repository),
		},
		Domains: convertPagesDomains(domains),
	})
}

// UpdatePagesConfig updates the pages configuration for a repository
func UpdatePagesConfig(ctx *context.APIContext) {
	// swagger:operation PUT /repos/{owner}/{repo}/pages repository repoUpdatePages
	// ---
	// summary: Update pages configuration for a repository
	// produces:
	// - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/CreatePagesConfigOption"
	// responses:
	//   "200":
	//     "$ref": "#/responses/PagesConfig"
	//   "403":
	//     "$ref": "#/responses/forbidden"
	//   "404":
	//     "$ref": "#/responses/notFound"

	form := web.GetForm(ctx).(*api.CreatePagesConfigOption)

	if form.Enabled {
		if err := pages_service.EnablePages(ctx, ctx.Repo.Repository, form.Template); err != nil {
			ctx.APIErrorInternal(err)
			return
		}
	} else {
		if err := pages_service.DisablePages(ctx, ctx.Repo.Repository); err != nil {
			ctx.APIErrorInternal(err)
			return
		}
	}

	ctx.JSON(http.StatusOK, &api.PagesConfig{
		Enabled:   form.Enabled,
		Template:  form.Template,
		Subdomain: pages_service.GetPagesSubdomain(ctx.Repo.Repository),
		URL:       pages_service.GetPagesURL(ctx.Repo.Repository),
	})
}

// DeletePagesConfig disables pages for a repository
func DeletePagesConfig(ctx *context.APIContext) {
	// swagger:operation DELETE /repos/{owner}/{repo}/pages repository repoDeletePages
	// ---
	// summary: Disable pages for a repository
	// produces:
	// - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"
	//   "403":
	//     "$ref": "#/responses/forbidden"
	//   "404":
	//     "$ref": "#/responses/notFound"

	if err := pages_service.DisablePages(ctx, ctx.Repo.Repository); err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	// Also delete all custom domains
	if err := repo_model.DeletePagesDomainsByRepoID(ctx, ctx.Repo.Repository.ID); err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	ctx.Status(http.StatusNoContent)
}

// ListPagesDomains returns all custom domains for a repository's pages
func ListPagesDomains(ctx *context.APIContext) {
	// swagger:operation GET /repos/{owner}/{repo}/pages/domains repository repoListPagesDomains
	// ---
	// summary: List custom domains for repository pages
	// produces:
	// - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// responses:
	//   "200":
	//     "$ref": "#/responses/PagesDomainList"
	//   "404":
	//     "$ref": "#/responses/notFound"

	domains, err := pages_service.GetPagesDomains(ctx, ctx.Repo.Repository.ID)
	if err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	ctx.JSON(http.StatusOK, convertPagesDomains(domains))
}

// AddPagesDomain adds a custom domain for pages
func AddPagesDomain(ctx *context.APIContext) {
	// swagger:operation POST /repos/{owner}/{repo}/pages/domains repository repoAddPagesDomain
	// ---
	// summary: Add a custom domain for repository pages
	// produces:
	// - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/AddPagesDomainOption"
	// responses:
	//   "201":
	//     "$ref": "#/responses/PagesDomain"
	//   "403":
	//     "$ref": "#/responses/forbidden"
	//   "404":
	//     "$ref": "#/responses/notFound"
	//   "422":
	//     "$ref": "#/responses/validationError"

	form := web.GetForm(ctx).(*api.AddPagesDomainOption)

	domain, err := pages_service.AddPagesDomain(ctx, ctx.Repo.Repository.ID, form.Domain, form.SSLExternal)
	if err != nil {
		if repo_model.IsErrPagesDomainAlreadyExist(err) {
			ctx.APIError(http.StatusUnprocessableEntity, "Domain already exists")
			return
		}
		ctx.APIErrorInternal(err)
		return
	}

	ctx.JSON(http.StatusCreated, convertPagesDomain(domain))
}

// DeletePagesDomain removes a custom domain
func DeletePagesDomain(ctx *context.APIContext) {
	// swagger:operation DELETE /repos/{owner}/{repo}/pages/domains/{domain} repository repoDeletePagesDomain
	// ---
	// summary: Remove a custom domain for repository pages
	// produces:
	// - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: domain
	//   in: path
	//   description: the domain to remove
	//   type: string
	//   required: true
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"
	//   "403":
	//     "$ref": "#/responses/forbidden"
	//   "404":
	//     "$ref": "#/responses/notFound"

	domainName := ctx.PathParam("domain")

	// Find the domain
	domain, err := repo_model.GetPagesDomainByDomain(ctx, domainName)
	if err != nil {
		if repo_model.IsErrPagesDomainNotExist(err) {
			ctx.APIErrorNotFound()
			return
		}
		ctx.APIErrorInternal(err)
		return
	}

	// Verify domain belongs to this repo
	if domain.RepoID != ctx.Repo.Repository.ID {
		ctx.APIErrorNotFound()
		return
	}

	if err := repo_model.DeletePagesDomain(ctx, domain.ID); err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	ctx.Status(http.StatusNoContent)
}

// VerifyPagesDomain verifies a custom domain
func VerifyPagesDomain(ctx *context.APIContext) {
	// swagger:operation POST /repos/{owner}/{repo}/pages/domains/{domain}/verify repository repoVerifyPagesDomain
	// ---
	// summary: Verify a custom domain for repository pages
	// produces:
	// - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: domain
	//   in: path
	//   description: the domain to verify
	//   type: string
	//   required: true
	// responses:
	//   "200":
	//     "$ref": "#/responses/PagesDomain"
	//   "403":
	//     "$ref": "#/responses/forbidden"
	//   "404":
	//     "$ref": "#/responses/notFound"

	domainName := ctx.PathParam("domain")

	// Find the domain
	domain, err := repo_model.GetPagesDomainByDomain(ctx, domainName)
	if err != nil {
		if repo_model.IsErrPagesDomainNotExist(err) {
			ctx.APIErrorNotFound()
			return
		}
		ctx.APIErrorInternal(err)
		return
	}

	// Verify domain belongs to this repo
	if domain.RepoID != ctx.Repo.Repository.ID {
		ctx.APIErrorNotFound()
		return
	}

	if err := pages_service.VerifyDomain(ctx, domain.ID); err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	// Reload domain
	domain, err = repo_model.GetPagesDomainByID(ctx, domain.ID)
	if err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	ctx.JSON(http.StatusOK, convertPagesDomain(domain))
}

// Helper functions

func getTemplateOrDefault(_ any) string {
	// TODO: Extract template from config when supported
	return "simple"
}

func convertPagesDomain(domain *repo_model.PagesDomain) *api.PagesDomain {
	return &api.PagesDomain{
		ID:                domain.ID,
		Domain:            domain.Domain,
		Verified:          domain.Verified,
		VerificationToken: domain.VerificationToken,
		SSLStatus:         string(domain.SSLStatus),
		SSLCertExpiry:     domain.SSLCertExpiry.AsTime(),
		Created:           domain.CreatedUnix.AsTime(),
		VerifiedAt:        domain.VerifiedUnix.AsTime(),
	}
}

func convertPagesDomains(domains []*repo_model.PagesDomain) []*api.PagesDomain {
	result := make([]*api.PagesDomain, len(domains))
	for i, d := range domains {
		result[i] = convertPagesDomain(d)
	}
	return result
}
