package panel

import (
	"database/sql"
	"net/http"
	"strconv"
	"strings"

	c "github.com/Azareal/Gosora/common"
	p "github.com/Azareal/Gosora/common/phrases"
)

func Groups(w http.ResponseWriter, r *http.Request, u c.User) c.RouteError {
	bPage, ferr := buildBasePage(w, r, &u, "groups", "groups")
	if ferr != nil {
		return ferr
	}
	page, _ := strconv.Atoi(r.FormValue("page"))
	perPage := 15
	offset, page, lastPage := c.PageOffset(bPage.Stats.Groups, page, perPage)

	// Skip the 'Unknown' group
	offset++

	var count int
	var groupList []c.GroupAdmin
	groups, _ := c.Groups.GetRange(offset, 0)
	for _, g := range groups {
		if count == perPage {
			break
		}
		var rank, rankClass string
		canDelete := false

		// TODO: Localise this
		switch {
		case g.IsAdmin:
			rank = "Admin"
			rankClass = "admin"
		case g.IsMod:
			rank = "Mod"
			rankClass = "mod"
		case g.IsBanned:
			rank = "Banned"
			rankClass = "banned"
		case g.ID == 6:
			rank = "Guest"
			rankClass = "guest"
		default:
			rank = "Member"
			rankClass = "member"
		}

		canEdit := u.Perms.EditGroup && (!g.IsAdmin || u.Perms.EditGroupAdmin) && (!g.IsMod || u.Perms.EditGroupSuperMod)
		groupList = append(groupList, c.GroupAdmin{g.ID, g.Name, rank, rankClass, canEdit, canDelete})
		count++
	}

	pageList := c.Paginate(page, lastPage, 5)
	pi := c.PanelGroupPage{bPage, groupList, c.Paginator{pageList, page, lastPage}}
	return renderTemplate("panel", w, r, bPage.Header, c.Panel{bPage, "", "", "panel_groups", &pi})
}

func GroupsEdit(w http.ResponseWriter, r *http.Request, user c.User, sgid string) c.RouteError {
	basePage, ferr := buildBasePage(w, r, &user, "edit_group", "groups")
	if ferr != nil {
		return ferr
	}
	if !user.Perms.EditGroup {
		return c.NoPermissions(w, r, user)
	}

	gid, err := strconv.Atoi(sgid)
	if err != nil {
		return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, user)
	}
	g, err := c.Groups.Get(gid)
	if err == sql.ErrNoRows {
		//log.Print("aaaaa monsters")
		return c.NotFound(w, r, basePage.Header)
	}
	ferr = groupCheck(w, r, user, g, err)
	if ferr != nil {
		return ferr
	}

	var rank string
	switch {
	case g.IsAdmin:
		rank = "Admin"
	case g.IsMod:
		rank = "Mod"
	case g.IsBanned:
		rank = "Banned"
	case g.ID == 6:
		rank = "Guest"
	default:
		rank = "Member"
	}
	disableRank := !user.Perms.EditGroupGlobalPerms || (g.ID == 6)

	pi := c.PanelEditGroupPage{basePage, g.ID, g.Name, g.Tag, rank, disableRank}
	return renderTemplate("panel_group_edit", w, r, basePage.Header, pi)
}

func GroupsEditPromotions(w http.ResponseWriter, r *http.Request, user c.User, sgid string) c.RouteError {
	basePage, ferr := buildBasePage(w, r, &user, "edit_group", "groups")
	if ferr != nil {
		return ferr
	}
	if !user.Perms.EditGroup {
		return c.NoPermissions(w, r, user)
	}

	gid, err := strconv.Atoi(sgid)
	if err != nil {
		return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, user)
	}
	g, err := c.Groups.Get(gid)
	if err == sql.ErrNoRows {
		//log.Print("aaaaa monsters")
		return c.NotFound(w, r, basePage.Header)
	}
	ferr = groupCheck(w, r, user, g, err)
	if ferr != nil {
		return ferr
	}

	promotions, err := c.GroupPromotions.GetByGroup(g.ID)
	if err != sql.ErrNoRows && err != nil {
		return c.InternalError(err, w, r)
	}
	promoteExt := make([]*c.GroupPromotionExtend, len(promotions))
	for i, promote := range promotions {
		fg, err := c.Groups.Get(promote.From)
		if err == sql.ErrNoRows {
			fg = &c.Group{Name: "Deleted Group"}
		} else if err != nil {
			return c.InternalError(err, w, r)
		}
		tg, err := c.Groups.Get(promote.To)
		if err == sql.ErrNoRows {
			tg = &c.Group{Name: "Deleted Group"}
		} else if err != nil {
			return c.InternalError(err, w, r)
		}
		promoteExt[i] = &c.GroupPromotionExtend{promote, fg, tg}
	}

	// ? - Should we stop admins from deleting all the groups? Maybe, protect the group they're currently using?
	groups, err := c.Groups.GetRange(1, 0) // ? - 0 = Go to the end
	if err != nil {
		return c.InternalError(err, w, r)
	}

	var groupList []*c.Group
	for _, group := range groups {
		if !user.Perms.EditUserGroupAdmin && group.IsAdmin {
			continue
		}
		if !user.Perms.EditUserGroupSuperMod && group.IsMod {
			continue
		}
		groupList = append(groupList, group)
	}

	pi := c.PanelEditGroupPromotionsPage{basePage, g.ID, g.Name, promoteExt, groupList}
	return renderTemplate("panel_group_edit_promotions", w, r, basePage.Header, pi)
}

func groupCheck(w http.ResponseWriter, r *http.Request, user c.User, g *c.Group, err error) c.RouteError {
	if err == sql.ErrNoRows {
		return c.LocalError("No such group.", w, r, user)
	} else if err != nil {
		return c.InternalError(err, w, r)
	}
	if g.IsAdmin && !user.Perms.EditGroupAdmin {
		return c.LocalError(p.GetErrorPhrase("panel_groups_cannot_edit_admin"), w, r, user)
	}
	if g.IsMod && !user.Perms.EditGroupSuperMod {
		return c.LocalError(p.GetErrorPhrase("panel_groups_cannot_edit_supermod"), w, r, user)
	}
	return nil
}

func GroupsPromotionsCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User, sgid string) c.RouteError {
	if !user.Perms.EditGroup {
		return c.NoPermissions(w, r, user)
	}
	gid, err := strconv.Atoi(sgid)
	if err != nil {
		return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, user)
	}

	from, err := strconv.Atoi(r.FormValue("from"))
	if err != nil {
		return c.LocalError("from must be integer", w, r, user)
	}
	to, err := strconv.Atoi(r.FormValue("to"))
	if err != nil {
		return c.LocalError("to must be integer", w, r, user)
	}
	if from == to {
		return c.LocalError("the from group and to group cannot be the same", w, r, user)
	}
	twoWay := r.FormValue("two-way") == "1"

	level, err := strconv.Atoi(r.FormValue("level"))
	if err != nil {
		return c.LocalError("level must be integer", w, r, user)
	}
	posts, err := strconv.Atoi(r.FormValue("posts"))
	if err != nil {
		return c.LocalError("posts must be integer", w, r, user)
	}

	regHours, err := strconv.Atoi(r.FormValue("registered_hours"))
	if err != nil {
		return c.LocalError("registered_hours must be integer", w, r, user)
	}
	regDays, err := strconv.Atoi(r.FormValue("registered_days"))
	if err != nil {
		return c.LocalError("registered_days must be integer", w, r, user)
	}
	regMonths, err := strconv.Atoi(r.FormValue("registered_months"))
	if err != nil {
		return c.LocalError("registered_months must be integer", w, r, user)
	}
	regMinutes := (regHours * 60) + (regDays * 24 * 60) + (regMonths * 30 * 24 * 60)

	g, err := c.Groups.Get(from)
	ferr := groupCheck(w, r, user, g, err)
	if err != nil {
		return ferr
	}
	g, err = c.Groups.Get(to)
	ferr = groupCheck(w, r, user, g, err)
	if err != nil {
		return ferr
	}
	pid, err := c.GroupPromotions.Create(from, to, twoWay, level, posts, regMinutes)
	if err != nil {
		return c.InternalError(err, w, r)
	}
	err = c.AdminLogs.Create("create", pid, "group_promotion", user.GetIP(), user.ID)
	if err != nil {
		return c.InternalError(err, w, r)
	}

	http.Redirect(w, r, "/panel/groups/edit/promotions/"+strconv.Itoa(gid), http.StatusSeeOther)
	return nil
}

func GroupsPromotionsDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, sspl string) c.RouteError {
	if !user.Perms.EditGroup {
		return c.NoPermissions(w, r, user)
	}
	spl := strings.Split(sspl, "-")
	if len(spl) < 2 {
		return c.LocalError("need two params", w, r, user)
	}
	gid, err := strconv.Atoi(spl[0])
	if err != nil {
		return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, user)
	}
	pid, err := strconv.Atoi(spl[1])
	if err != nil {
		return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, user)
	}

	pro, err := c.GroupPromotions.Get(pid)
	if err == sql.ErrNoRows {
		return c.LocalError("That group promotion doesn't exist", w, r, user)
	} else if err != nil {
		return c.InternalError(err, w, r)
	}

	g, err := c.Groups.Get(pro.From)
	ferr := groupCheck(w, r, user, g, err)
	if err != nil {
		return ferr
	}
	g, err = c.Groups.Get(pro.To)
	ferr = groupCheck(w, r, user, g, err)
	if err != nil {
		return ferr
	}
	err = c.GroupPromotions.Delete(pid)
	if err != nil {
		return c.InternalError(err, w, r)
	}
	err = c.AdminLogs.Create("delete", pid, "group_promotion", user.GetIP(), user.ID)
	if err != nil {
		return c.InternalError(err, w, r)
	}

	http.Redirect(w, r, "/panel/groups/edit/promotions/"+strconv.Itoa(gid), http.StatusSeeOther)
	return nil
}

func GroupsEditPerms(w http.ResponseWriter, r *http.Request, user c.User, sgid string) c.RouteError {
	basePage, ferr := buildBasePage(w, r, &user, "edit_group", "groups")
	if ferr != nil {
		return ferr
	}
	if !user.Perms.EditGroup {
		return c.NoPermissions(w, r, user)
	}
	gid, err := strconv.Atoi(sgid)
	if err != nil {
		return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, user)
	}

	g, err := c.Groups.Get(gid)
	if err == sql.ErrNoRows {
		//log.Print("aaaaa monsters")
		return c.NotFound(w, r, basePage.Header)
	} else if err != nil {
		return c.InternalError(err, w, r)
	}
	if g.IsAdmin && !user.Perms.EditGroupAdmin {
		return c.LocalError(p.GetErrorPhrase("panel_groups_cannot_edit_admin"), w, r, user)
	}
	if g.IsMod && !user.Perms.EditGroupSuperMod {
		return c.LocalError(p.GetErrorPhrase("panel_groups_cannot_edit_supermod"), w, r, user)
	}

	// TODO: Load the phrases in bulk for efficiency?
	var localPerms []c.NameLangToggle
	addPerm := func(permStr string, perm bool) {
		localPerms = append(localPerms, c.NameLangToggle{permStr, p.GetPermPhrase(permStr), perm})
	}

	addPerm("ViewTopic", g.Perms.ViewTopic)
	addPerm("LikeItem", g.Perms.LikeItem)
	addPerm("CreateTopic", g.Perms.CreateTopic)
	//<--
	addPerm("EditTopic", g.Perms.EditTopic)
	addPerm("DeleteTopic", g.Perms.DeleteTopic)
	addPerm("CreateReply", g.Perms.CreateReply)
	addPerm("EditReply", g.Perms.EditReply)
	addPerm("DeleteReply", g.Perms.DeleteReply)
	addPerm("PinTopic", g.Perms.PinTopic)
	addPerm("CloseTopic", g.Perms.CloseTopic)
	addPerm("MoveTopic", g.Perms.MoveTopic)

	var globalPerms []c.NameLangToggle
	addPerm = func(permStr string, perm bool) {
		globalPerms = append(globalPerms, c.NameLangToggle{permStr, p.GetPermPhrase(permStr), perm})
	}

	addPerm("UploadFiles", g.Perms.UploadFiles)
	addPerm("UploadAvatars", g.Perms.UploadAvatars)
	addPerm("UseConvos", g.Perms.UseConvos)
	addPerm("UseConvosOnlyWithMod", g.Perms.UseConvosOnlyWithMod)
	addPerm("CreateProfileReply", g.Perms.CreateProfileReply)
	addPerm("AutoEmbed", g.Perms.AutoEmbed)

	var modPerms []c.NameLangToggle
	addPerm = func(permStr string, perm bool) {
		modPerms = append(modPerms, c.NameLangToggle{permStr, p.GetPermPhrase(permStr), perm})
	}

	addPerm("BanUsers", g.Perms.BanUsers)
	addPerm("ActivateUsers", g.Perms.ActivateUsers)
	addPerm("EditUser", g.Perms.EditUser)
	addPerm("EditUserEmail", g.Perms.EditUserEmail)
	addPerm("EditUserPassword", g.Perms.EditUserPassword)
	addPerm("EditUserGroup", g.Perms.EditUserGroup)
	addPerm("EditUserGroupSuperMod", g.Perms.EditUserGroupSuperMod)
	addPerm("EditUserGroupAdmin", g.Perms.EditUserGroupAdmin)
	addPerm("EditGroup", g.Perms.EditGroup)
	addPerm("EditGroupLocalPerms", g.Perms.EditGroupLocalPerms)
	addPerm("EditGroupGlobalPerms", g.Perms.EditGroupGlobalPerms)
	addPerm("EditGroupSuperMod", g.Perms.EditGroupSuperMod)
	addPerm("EditGroupAdmin", g.Perms.EditGroupAdmin)
	addPerm("ManageForums", g.Perms.ManageForums)
	addPerm("EditSettings", g.Perms.EditSettings)
	addPerm("ManageThemes", g.Perms.ManageThemes)
	addPerm("ManagePlugins", g.Perms.ManagePlugins)
	addPerm("ViewAdminLogs", g.Perms.ViewAdminLogs)
	addPerm("ViewIPs", g.Perms.ViewIPs)

	pi := c.PanelEditGroupPermsPage{basePage, g.ID, g.Name, localPerms, globalPerms, modPerms}
	return renderTemplate("panel_group_edit_perms", w, r, basePage.Header, pi)
}

func GroupsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, sgid string) c.RouteError {
	_, ferr := c.SimplePanelUserCheck(w, r, &user)
	if ferr != nil {
		return ferr
	}
	if !user.Perms.EditGroup {
		return c.NoPermissions(w, r, user)
	}
	gid, err := strconv.Atoi(sgid)
	if err != nil {
		return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user)
	}
	group, err := c.Groups.Get(gid)
	if err == sql.ErrNoRows {
		//log.Print("aaaaa monsters")
		return c.NotFound(w, r, nil)
	}
	ferr = groupCheck(w, r, user, group, err)
	if ferr != nil {
		return ferr
	}

	name := r.FormValue("name")
	if name == "" {
		return c.LocalError(p.GetErrorPhrase("panel_groups_need_name"), w, r, user)
	}
	tag := r.FormValue("tag")
	rank := r.FormValue("type")

	var originalRank string
	// TODO: Use a switch for this
	switch {
	case group.IsAdmin:
		originalRank = "Admin"
	case group.IsMod:
		originalRank = "Mod"
	case group.IsBanned:
		originalRank = "Banned"
	case group.ID == 6:
		originalRank = "Guest"
	default:
		originalRank = "Member"
	}

	if rank != originalRank && originalRank != "Guest" {
		if !user.Perms.EditGroupGlobalPerms {
			return c.LocalError(p.GetErrorPhrase("panel_groups_cannot_edit_group_type"), w, r, user)
		}
		switch rank {
		case "Admin":
			if !user.Perms.EditGroupAdmin {
				return c.LocalError(p.GetErrorPhrase("panel_groups_edit_cannot_designate_admin"), w, r, user)
			}
			err = group.ChangeRank(true, true, false)
		case "Mod":
			if !user.Perms.EditGroupSuperMod {
				return c.LocalError(p.GetErrorPhrase("panel_groups_edit_cannot_designate_supermod"), w, r, user)
			}
			err = group.ChangeRank(false, true, false)
		case "Banned":
			err = group.ChangeRank(false, false, true)
		case "Guest":
			return c.LocalError(p.GetErrorPhrase("panel_groups_cannot_be_guest"), w, r, user)
		case "Member":
			err = group.ChangeRank(false, false, false)
		default:
			return c.LocalError(p.GetErrorPhrase("panel_groups_invalid_group_type"), w, r, user)
		}
		if err != nil {
			return c.InternalError(err, w, r)
		}
	}

	err = group.Update(name, tag)
	if err != nil {
		return c.InternalError(err, w, r)
	}
	err = c.AdminLogs.Create("edit", group.ID, "group", user.GetIP(), user.ID)
	if err != nil {
		return c.InternalError(err, w, r)
	}

	http.Redirect(w, r, "/panel/groups/edit/"+strconv.Itoa(gid), http.StatusSeeOther)
	return nil
}

func GroupsEditPermsSubmit(w http.ResponseWriter, r *http.Request, user c.User, sgid string) c.RouteError {
	_, ferr := c.SimplePanelUserCheck(w, r, &user)
	if ferr != nil {
		return ferr
	}
	if !user.Perms.EditGroup {
		return c.NoPermissions(w, r, user)
	}
	gid, err := strconv.Atoi(sgid)
	if err != nil {
		return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user)
	}

	group, err := c.Groups.Get(gid)
	if err == sql.ErrNoRows {
		//log.Print("aaaaa monsters o.o")
		return c.NotFound(w, r, nil)
	}
	ferr = groupCheck(w, r, user, group, err)
	if ferr != nil {
		return ferr
	}

	// TODO: Don't unset perms we don't have permission to set?
	pmap := make(map[string]bool)
	pCheck := func(hasPerm bool, perms []string) {
		if hasPerm {
			for _, perm := range perms {
				pvalue := r.PostFormValue("perm-" + perm)
				pmap[perm] = (pvalue == "1")
			}
		}
	}
	pCheck(user.Perms.EditGroupLocalPerms, c.LocalPermList)
	pCheck(user.Perms.EditGroupGlobalPerms, c.GlobalPermList)

	err = group.UpdatePerms(pmap)
	if err != nil {
		return c.InternalError(err, w, r)
	}
	err = c.AdminLogs.Create("edit", group.ID, "group", user.GetIP(), user.ID)
	if err != nil {
		return c.InternalError(err, w, r)
	}

	http.Redirect(w, r, "/panel/groups/edit/perms/"+strconv.Itoa(gid), http.StatusSeeOther)
	return nil
}

func GroupsCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
	_, ferr := c.SimplePanelUserCheck(w, r, &user)
	if ferr != nil {
		return ferr
	}
	if !user.Perms.EditGroup {
		return c.NoPermissions(w, r, user)
	}

	groupName := r.PostFormValue("name")
	if groupName == "" {
		return c.LocalError(p.GetErrorPhrase("panel_groups_need_name"), w, r, user)
	}
	groupTag := r.PostFormValue("tag")

	var isAdmin, isMod, isBanned bool
	if user.Perms.EditGroupGlobalPerms {
		switch r.PostFormValue("type") {
		case "Admin":
			if !user.Perms.EditGroupAdmin {
				return c.LocalError(p.GetErrorPhrase("panel_groups_create_cannot_designate_admin"), w, r, user)
			}
			isAdmin = true
			isMod = true
		case "Mod":
			if !user.Perms.EditGroupSuperMod {
				return c.LocalError(p.GetErrorPhrase("panel_groups_create_cannot_designate_supermod"), w, r, user)
			}
			isMod = true
		case "Banned":
			isBanned = true
		}
	}

	gid, err := c.Groups.Create(groupName, groupTag, isAdmin, isMod, isBanned)
	if err != nil {
		return c.InternalError(err, w, r)
	}
	err = c.AdminLogs.Create("create", gid, "group", user.GetIP(), user.ID)
	if err != nil {
		return c.InternalError(err, w, r)
	}

	http.Redirect(w, r, "/panel/groups/edit/"+strconv.Itoa(gid), http.StatusSeeOther)
	return nil
}