package gist

import (
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/google/uuid"
	"github.com/thomiceli/opengist/internal/config"
	"github.com/thomiceli/opengist/internal/db"
	"github.com/thomiceli/opengist/internal/git"
	"github.com/thomiceli/opengist/internal/i18n"
	"github.com/thomiceli/opengist/internal/validator"
	"github.com/thomiceli/opengist/internal/web/context"
)

func Create(ctx *context.Context) error {
	ctx.SetData("htmlTitle", ctx.TrH("gist.new.create-a-new-gist"))
	return ctx.Html("create.html")
}

func ProcessCreate(ctx *context.Context) error {
	isCreate := ctx.Request().URL.Path == "/"

	dto := new(db.GistDTO)
	var gist *db.Gist

	if isCreate {
		ctx.SetData("htmlTitle", ctx.TrH("gist.new.create-a-new-gist"))
	} else {
		gist = ctx.GetData("gist").(*db.Gist)
		ctx.SetData("htmlTitle", ctx.TrH("gist.edit.edit-gist", gist.Title))
	}

	err := ctx.Bind(dto)
	if err != nil {
		return ctx.ErrorRes(400, ctx.Tr("error.cannot-bind-data"), err)
	}

	dto.Files = make([]db.FileDTO, 0)

	names := dto.Name
	contents := dto.Content

	// Process files from text editors
	for i, content := range contents {
		if content == "" {
			continue
		}
		name := git.CleanTreePathName(names[i])
		if name == "" {
			name = "gistfile" + strconv.Itoa(len(dto.Files)+1) + ".txt"
		}

		escapedValue, err := url.PathUnescape(content)
		if err != nil {
			return ctx.ErrorRes(400, ctx.Tr("error.invalid-character-unescaped"), err)
		}

		dto.Files = append(dto.Files, db.FileDTO{
			Filename: strings.TrimSpace(name),
			Content:  escapedValue,
		})
	}

	// Process uploaded files from UUID arrays
	fileUUIDs := dto.UploadedFilesUUID
	fileFilenames := dto.UploadedFilesNames
	if len(fileUUIDs) == len(fileFilenames) {
		for i, fileUUID := range fileUUIDs {
			if !uuidRegex.MatchString(filepath.Base(fileUUID)) {
				continue
			}
			filePath := filepath.Join(config.GetHomeDir(), "uploads", fileUUID)

			if _, err := os.Stat(filePath); err != nil {
				continue
			}

			name := git.CleanTreePathName(fileFilenames[i])
			if name == "" {
				name = "gistfile" + strconv.Itoa(len(dto.Files)+1) + ".txt"
			}

			dto.Files = append(dto.Files, db.FileDTO{
				Filename:   name,
				SourcePath: filePath,
				Content:    "", // Empty since we're using SourcePath
			})
		}
	}

	// Process binary file operations (edit mode)
	binaryOldNames := dto.BinaryFileOldName
	binaryNewNames := dto.BinaryFileNewName
	if len(binaryOldNames) == len(binaryNewNames) {
		for i, oldName := range binaryOldNames {
			newName := git.CleanTreePathName(binaryNewNames[i])

			if newName == "" { // deletion
				continue
			}

			if !isCreate {
				gistOld := ctx.GetData("gist").(*db.Gist)

				fileContent, _, err := git.GetFileContent(gistOld.User.Username, gistOld.Uuid, "HEAD", oldName, false)
				if err != nil {
					continue
				}

				dto.Files = append(dto.Files, db.FileDTO{
					Filename: newName,
					Content:  fileContent,
					Binary:   true,
				})
			}
		}
	}
	ctx.SetData("dto", dto)

	err = ctx.Validate(dto)
	if err != nil {
		ctx.AddFlash(validator.ValidationMessages(&err, ctx.GetData("locale").(*i18n.Locale)), "error")
		if isCreate {
			return ctx.HtmlWithCode(400, "create.html")
		} else {
			files, err := gist.Files("HEAD", false)
			if err != nil {
				return ctx.ErrorRes(500, "Error fetching files", err)
			}
			ctx.SetData("files", files)
			return ctx.HtmlWithCode(400, "edit.html")
		}
	}

	if isCreate {
		gist = dto.ToGist()
	} else {
		gist = dto.ToExistingGist(gist)
	}

	user := ctx.User
	gist.NbFiles = len(dto.Files)

	if isCreate {
		uuidGist, err := uuid.NewRandom()
		if err != nil {
			return ctx.ErrorRes(500, "Error creating an UUID", err)
		}
		gist.Uuid = strings.ReplaceAll(uuidGist.String(), "-", "")

		gist.UserID = user.ID
		gist.User = *user
	}

	if gist.Title == "" {
		if dto.Files[0].Filename == "" {
			gist.Title = "gist:" + gist.Uuid
		} else {
			gist.Title = dto.Files[0].Filename
		}
	}

	if err = gist.InitRepository(); err != nil {
		return ctx.ErrorRes(500, "Error creating the repository", err)
	}

	if err = gist.AddAndCommitFiles(&dto.Files); err != nil {
		return ctx.ErrorRes(500, "Error adding and committing files", err)
	}

	if isCreate {
		if err = gist.Create(); err != nil {
			return ctx.ErrorRes(500, "Error creating the gist", err)
		}
	} else {
		if err = gist.Update(); err != nil {
			return ctx.ErrorRes(500, "Error updating the gist", err)
		}
	}

	gist.AddInIndex()
	gist.UpdateLanguages()
	if err = gist.UpdatePreviewAndCount(true); err != nil {
		return ctx.ErrorRes(500, "Error updating preview and count", err)
	}

	return ctx.RedirectTo("/" + user.Username + "/" + gist.Identifier())
}
