package friend

import (
	"errors"
	"honoka-chan/internal/middleware"
	unitmodel "honoka-chan/internal/model/unit"
	usermodel "honoka-chan/internal/model/user"
	"honoka-chan/internal/router"
	friendschema "honoka-chan/internal/schema/friend"
	"honoka-chan/internal/session"
	honokautils "honoka-chan/internal/utils"
	"time"

	"github.com/gin-gonic/gin"
)

type friendSearchRow struct {
	UserID                 int    `xorm:"user_id"`
	InviteCode             string `xorm:"invite_code"`
	UserName               string `xorm:"user_name"`
	UserLevel              int    `xorm:"user_level"`
	UserDesc               string `xorm:"user_desc"`
	EnergyMax              int    `xorm:"energy_max"`
	AwardID                int    `xorm:"award_id"`
	LastLoginTime          int64  `xorm:"last_login_time"`
	CenterUnitOwningUserID int    `xorm:"center_unit_owning_user_id"`
	NaviUnitOwningUserID   int    `xorm:"navi_unit_owning_user_id"`
}

func search(ctx *gin.Context) {
	ss := session.Get(ctx)
	defer ss.Finalize()

	req := friendschema.SearchReq{}
	err := honokautils.ParseRequestData(ctx, &req)
	if ss.CheckErr(err) {
		return
	}

	targetUserID, err := resolveUserIDByInviteCode(ss, req.InviteCode)
	if ss.CheckErr(err) {
		return
	}

	row := friendSearchRow{}
	has, err := ss.UserEng.Table(new(usermodel.UserPref)).Alias("up").
		Join("LEFT", "users u", "u.user_id = up.user_id").
		Join("LEFT", "user_deck ud", "ud.user_id = up.user_id AND ud.main_flag = 1").
		Join("LEFT", "user_deck_unit udu", "udu.user_deck_id = ud.id AND udu.position = 5").
		Select(`
			up.user_id,
			up.invite_code,
			up.user_name,
			up.user_level,
			up.user_desc,
			up.energy_max,
			up.award_id,
			u.last_login_time,
			COALESCE(udu.unit_owning_user_id, up.unit_owning_user_id) AS center_unit_owning_user_id,
			up.unit_owning_user_id AS navi_unit_owning_user_id
		`).
		Where("up.user_id = ?", targetUserID).
		Get(&row)
	if ss.CheckErr(err) {
		return
	}
	if !has {
		ss.CheckErr(errors.New("user not found"))
		return
	}

	unitCount, err := ss.UserEng.Table(new(usermodel.UserUnitData)).
		Where("user_id = ?", targetUserID).
		Count()
	if ss.CheckErr(err) {
		return
	}

	centerUnitInfo, err := buildCenterUnitInfo(ss, targetUserID, row.CenterUnitOwningUserID, row.AwardID)
	if ss.CheckErr(err) {
		return
	}

	costume, err := getSearchCostume(ss, targetUserID, row.NaviUnitOwningUserID, centerUnitInfo)
	if ss.CheckErr(err) {
		return
	}

	friendStatus, err := usermodel.ResolveClientFriendStatus(ss.UserEng, ss.UserID, targetUserID)
	if ss.CheckErr(err) {
		return
	}

	ss.Respond(friendschema.SearchResp{
		ResponseData: friendschema.SearchData{
			UserInfo: friendschema.SearchUserInfo{
				UserID:               row.UserID,
				Name:                 row.UserName,
				Level:                row.UserLevel,
				CostMax:              100,
				UnitMax:              5000,
				EnergyMax:            positiveIntOrDefault(row.EnergyMax, usermodel.DefaultUserEnergyMax),
				FriendMax:            99,
				UnitCnt:              unitCount,
				ElapsedTimeFromLogin: formatElapsedTime(row.LastLoginTime),
				Comment:              row.UserDesc,
			},
			CenterUnitInfo:  toSearchCenterUnitInfo(centerUnitInfo, costume),
			SettingAwardID:  row.AwardID,
			IsAlliance:      false,
			FriendStatus:    friendStatus,
			ServerTimestamp: time.Now().Unix(),
		},
		ReleaseInfo: []any{},
		StatusCode:  200,
	})
}

func init() {
	router.AddHandler("main.php", "POST", "/friend/search", middleware.Common, search)
}

func positiveIntOrDefault(value, fallback int) int {
	if value > 0 {
		return value
	}
	return fallback
}

func toSearchCenterUnitInfo(info friendschema.CenterUnitInfo, costume friendschema.SearchCostume) friendschema.SearchCenterUnitInfo {
	return friendschema.SearchCenterUnitInfo{
		UnitOwningUserID:           info.UnitOwningUserID,
		UnitID:                     info.UnitID,
		Exp:                        info.Exp,
		NextExp:                    info.NextExp,
		Level:                      info.Level,
		LevelLimitID:               info.LevelLimitID,
		MaxLevel:                   info.MaxLevel,
		Rank:                       info.Rank,
		MaxRank:                    info.MaxRank,
		Love:                       info.Love,
		MaxLove:                    info.MaxLove,
		UnitSkillLevel:             info.UnitSkillLevel,
		MaxHp:                      info.MaxHp,
		FavoriteFlag:               info.FavoriteFlag,
		DisplayRank:                info.DisplayRank,
		UnitSkillExp:               info.UnitSkillExp,
		UnitRemovableSkillCapacity: info.UnitRemovableSkillCapacity,
		Attribute:                  info.Attribute,
		Smile:                      info.Smile,
		Cute:                       info.Cute,
		Cool:                       info.Cool,
		IsLoveMax:                  info.IsLoveMax,
		IsLevelMax:                 info.IsLevelMax,
		IsRankMax:                  info.IsRankMax,
		IsSigned:                   info.IsSigned,
		IsSkillLevelMax:            info.IsSkillLevelMax,
		SettingAwardID:             info.SettingAwardID,
		RemovableSkillIds:          info.RemovableSkillIds,
		AccessoryInfo:              info.AccessoryInfo,
		Costume:                    costume,
	}
}

func getSearchCostume(ss *session.Session, targetUserID, unitOwningUserID int, fallback friendschema.CenterUnitInfo) (friendschema.SearchCostume, error) {
	costume := friendschema.SearchCostume{
		UnitID:    fallback.UnitID,
		IsRankMax: fallback.IsRankMax,
		IsSigned:  fallback.IsSigned,
	}
	if unitOwningUserID <= 0 {
		return costume, nil
	}

	unitData := unitmodel.UnitDataMap{}
	has, err := ss.GetBasicUnitInfo().
		Where("a.user_id = ?", targetUserID).
		Where("a.unit_owning_user_id = ?", unitOwningUserID).
		Get(&unitData)
	if err != nil {
		return costume, err
	}
	if !has {
		return costume, nil
	}

	return friendschema.SearchCostume{
		UnitID:    unitData.UnitID,
		IsRankMax: unitData.IsRankMax,
		IsSigned:  unitData.IsSigned,
	}, nil
}
