package v1

import (
	"bytes"
	"context"
	"crypto/md5"
	"fmt"
	"log/slog"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/GoYoko/web"
	"golang.org/x/time/rate"

	"github.com/chaitin/MonkeyCode/backend/config"
	"github.com/chaitin/MonkeyCode/backend/consts"
	"github.com/chaitin/MonkeyCode/backend/domain"
	"github.com/chaitin/MonkeyCode/backend/errcode"
	"github.com/chaitin/MonkeyCode/backend/internal/middleware"
	"github.com/chaitin/MonkeyCode/backend/pkg/session"
	"github.com/chaitin/MonkeyCode/backend/pkg/version"
	"github.com/chaitin/MonkeyCode/backend/pkg/vsix"
)

// CacheEntry 缓存条目
type CacheEntry struct {
	data      []byte
	createdAt time.Time
}

type UserHandler struct {
	usecase   domain.UserUsecase
	euse      domain.ExtensionUsecase
	duse      domain.DashboardUsecase
	buse      domain.BillingUsecase
	scuse     domain.SecurityScanningUsecase
	session   *session.Session
	logger    *slog.Logger
	cfg       *config.Config
	vsixCache map[string]*CacheEntry
	cacheMu   sync.RWMutex
	limiter   *rate.Limiter
}

func NewUserHandler(
	w *web.Web,
	usecase domain.UserUsecase,
	euse domain.ExtensionUsecase,
	scuse domain.SecurityScanningUsecase,
	duse domain.DashboardUsecase,
	buse domain.BillingUsecase,
	auth *middleware.AuthMiddleware,
	active *middleware.ActiveMiddleware,
	readonly *middleware.ReadOnlyMiddleware,
	session *session.Session,
	logger *slog.Logger,
	cfg *config.Config,
) *UserHandler {
	u := &UserHandler{
		usecase:   usecase,
		euse:      euse,
		duse:      duse,
		buse:      buse,
		scuse:     scuse,
		session:   session,
		logger:    logger,
		cfg:       cfg,
		vsixCache: make(map[string]*CacheEntry),
		limiter:   rate.NewLimiter(rate.Every(time.Duration(cfg.Extension.LimitSecond)*time.Second), cfg.Extension.Limit),
	}

	w.GET("/api/v1/static/vsix/:version", web.BaseHandler(u.VSIXDownload))
	w.GET("/api/v1/static/vsix", web.BaseHandler(u.VSIXDownload))
	w.POST("/api/v1/vscode/init-auth", web.BindHandler(u.VSCodeAuthInit))

	// admin
	admin := w.Group("/api/v1/admin")
	admin.POST("/login", web.BindHandler(u.AdminLogin))
	admin.GET("/setting", web.BaseHandler(u.GetSetting))
	admin.GET("/role", web.BaseHandler(u.ListRole))

	admin.Use(auth.Auth(), active.Active("admin"), readonly.Guard())
	admin.GET("/profile", web.BaseHandler(u.AdminProfile))
	admin.GET("/list", web.BaseHandler(u.AdminList, web.WithPage()))
	admin.GET("/login-history", web.BaseHandler(u.AdminLoginHistory, web.WithPage()))
	admin.PUT("/setting", web.BindHandler(u.UpdateSetting))
	admin.POST("/create", web.BindHandler(u.CreateAdmin))
	admin.POST("/logout", web.BaseHandler(u.AdminLogout))
	admin.DELETE("/delete", web.BaseHandler(u.DeleteAdmin))
	admin.GET("/export-completion-data", web.BaseHandler(u.ExportCompletionData))
	admin.POST("/role", web.BindHandler(u.GrantRole))

	// user
	g := w.Group("/api/v1/user")
	g.GET("/oauth/signup-or-in", web.BindHandler(u.OAuthSignUpOrIn))
	g.GET("/oauth/callback", web.BindHandler(u.OAuthCallback))
	g.POST("/register", web.BindHandler(u.Register))
	g.POST("/login", web.BindHandler(u.Login))

	g.Use(readonly.Guard())
	g.GET("/profile", web.BaseHandler(u.Profile), auth.UserAuth())
	g.PUT("/profile", web.BindHandler(u.UpdateProfile), auth.UserAuth())
	g.POST("/logout", web.BaseHandler(u.Logout), auth.UserAuth())

	g.Use(auth.Auth(), active.Active("admin"))

	g.PUT("/update", web.BindHandler(u.Update))
	g.DELETE("/delete", web.BaseHandler(u.Delete))
	g.GET("/invite", web.BaseHandler(u.Invite))
	g.GET("/list", web.BindHandler(u.List, web.WithPage()))
	g.GET("/login-history", web.BaseHandler(u.LoginHistory, web.WithPage()))

	// user dashboard
	d := w.Group("/api/v1/user/dashboard")
	d.Use(auth.UserAuth(), active.Active("user"))
	d.GET("/stat", web.BindHandler(u.UserStat))
	d.GET("/events", web.BaseHandler(u.UserEvents))
	d.GET("/heatmap", web.BaseHandler(u.UserHeatmap))

	// user record
	uc := w.Group("/api/v1/user/chat")
	uc.Use(auth.UserAuth(), active.Active("user"))
	uc.GET("/record", web.BindHandler(u.ListChatRecord, web.WithPage()))
	uc.GET("/info", web.BaseHandler(u.ChatInfo))

	cplt := w.Group("/api/v1/user/completion")
	cplt.Use(auth.UserAuth(), active.Active("user"))
	cplt.GET("/record", web.BindHandler(u.ListCompletionRecord, web.WithPage()))
	cplt.GET("/info", web.BaseHandler(u.CompletionInfo))

	// user security
	sc := w.Group("/api/v1/user/security")
	sc.Use(auth.UserAuth(), active.Active("user"))
	sc.GET("/scanning", web.BindHandler(u.SecurityList, web.WithPage()))
	sc.GET("/scanning/detail", web.BaseHandler(u.SecurityDetail))

	return u
}

func (h *UserHandler) VSCodeAuthInit(c *web.Context, req domain.VSCodeAuthInitReq) error {
	s, err := h.usecase.GetSetting(c.Request().Context())
	if err != nil {
		return err
	}
	req.BaseURL = h.cfg.GetBaseURL(c.Request(), s)
	resp, err := h.usecase.VSCodeAuthInit(c.Request().Context(), &req)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, resp)
}

// generateCacheKey 生成缓存键
func (h *UserHandler) generateCacheKey(version, baseUrl string) string {
	hash := md5.Sum([]byte(version + ":" + baseUrl))
	return fmt.Sprintf("%x", hash)
}

// cleanExpiredCache 清理过期缓存
func (h *UserHandler) cleanExpiredCache() {
	h.cacheMu.Lock()
	defer h.cacheMu.Unlock()

	now := time.Now()
	for key, entry := range h.vsixCache {
		// 缓存1小时后过期
		if now.Sub(entry.createdAt) > time.Hour {
			delete(h.vsixCache, key)
		}
	}
}

// VSIXDownload 下载VSCode插件
//
//	@Tags			User
//	@Summary		下载VSCode插件
//	@Description	下载VSCode插件
//	@ID				vsix-download
//	@Accept			json
//	@Produce		octet-stream
//	@Router			/api/v1/static/vsix [get]
func (h *UserHandler) VSIXDownload(c *web.Context) error {
	if !h.limiter.Allow() {
		return c.String(http.StatusTooManyRequests, "Too Many Requests")
	}

	s, err := h.usecase.GetSetting(c.Request().Context())
	if err != nil {
		return err
	}

	host := c.Request().Host
	h.logger.With("url", c.Request().URL).With("header", c.Request().Header).With("host", host).DebugContext(c.Request().Context(), "vsix download")
	cacheKey := h.generateCacheKey(version.Version, h.cfg.GetBaseURL(c.Request(), s))
	version := strings.Trim(version.Version, "v")

	h.cacheMu.RLock()
	if entry, exists := h.vsixCache[cacheKey]; exists {
		if time.Since(entry.createdAt) < time.Hour {
			h.cacheMu.RUnlock()

			disposition := fmt.Sprintf("attachment; filename=monkeycode-%s.vsix", version)
			c.Response().Header().Set("Content-Type", "application/octet-stream")
			c.Response().Header().Set("Content-Disposition", disposition)
			c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", len(entry.data)))

			_, err := c.Response().Writer.Write(entry.data)
			return err
		}
	}
	h.cacheMu.RUnlock()

	var buf bytes.Buffer
	if err := vsix.ChangeVsixEndpoint(fmt.Sprintf("/app/assets/vsix/monkeycode-%s.vsix", version), "extension/package.json", h.cfg.GetBaseURL(c.Request(), s), &buf); err != nil {
		return err
	}

	data := buf.Bytes()
	h.cacheMu.Lock()
	h.vsixCache[cacheKey] = &CacheEntry{
		data:      data,
		createdAt: time.Now(),
	}
	h.cacheMu.Unlock()

	go h.cleanExpiredCache()

	disposition := fmt.Sprintf("attachment; filename=monkeycode-%s.vsix", version)
	c.Response().Header().Set("Content-Type", "application/octet-stream")
	c.Response().Header().Set("Content-Disposition", disposition)
	c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))

	_, err = c.Response().Writer.Write(data)
	return err
}

// Login 用户登录
//
//	@Tags			User
//	@Summary		用户登录
//	@Description	用户登录
//	@ID				login
//	@Accept			json
//	@Produce		json
//	@Param			param	body		domain.LoginReq	true	"登录参数"
//	@Success		200		{object}	web.Resp{data=domain.LoginResp}
//	@Router			/api/v1/user/login [post]
func (h *UserHandler) Login(c *web.Context, req domain.LoginReq) error {
	req.IP = c.RealIP()
	resp, err := h.usecase.Login(c.Request().Context(), &req)
	if err != nil {
		return err
	}
	if req.Source == consts.LoginSourceBrowser {
		if _, err := h.session.Save(c, consts.UserSessionName, c.Request().Host, resp.User); err != nil {
			return err
		}
	}
	return c.Success(resp)
}

// Logout 用户登出
//
//	@Tags			User
//	@Summary		用户登出
//	@Description	用户登出
//	@ID				logout
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	web.Resp{}
//	@Router			/api/v1/user/logout [post]
func (h *UserHandler) Logout(c *web.Context) error {
	if err := h.session.Del(c, consts.UserSessionName); err != nil {
		return err
	}
	return c.Success(nil)
}

// Update 更新用户
//
//	@Tags			User
//	@Summary		更新用户
//	@Description	更新用户
//	@ID				update-user
//	@Accept			json
//	@Produce		json
//	@Param			param	body		domain.UpdateUserReq	true	"更新用户参数"
//	@Success		200		{object}	web.Resp{data=domain.User}
//	@Router			/api/v1/user/update [put]
func (h *UserHandler) Update(c *web.Context, req domain.UpdateUserReq) error {
	resp, err := h.usecase.Update(c.Request().Context(), &req)
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// Delete 删除用户
//
//	@Tags			User
//	@Summary		删除用户
//	@Description	删除用户
//	@ID				delete-user
//	@Accept			json
//	@Produce		json
//	@Param			id	query		string	true	"用户ID"
//	@Success		200	{object}	web.Resp{data=nil}
//	@Router			/api/v1/user/delete [delete]
func (h *UserHandler) Delete(c *web.Context) error {
	err := h.usecase.Delete(c.Request().Context(), c.QueryParam("id"))
	if err != nil {
		return err
	}
	return c.Success(nil)
}

// DeleteAdmin 删除管理员
//
//	@Tags			Admin
//	@Summary		删除管理员
//	@Description	删除管理员
//	@ID				delete-admin
//	@Accept			json
//	@Produce		json
//	@Param			id	query		string	true	"管理员ID"
//	@Success		200	{object}	web.Resp{data=nil}
//	@Router			/api/v1/admin/delete [delete]
func (h *UserHandler) DeleteAdmin(c *web.Context) error {
	err := h.usecase.DeleteAdmin(c.Request().Context(), c.QueryParam("id"))
	if err != nil {
		return err
	}
	return c.Success(nil)
}

// AdminLogin 管理员登录
//
//	@Tags			Admin
//	@Summary		管理员登录
//	@Description	管理员登录
//	@ID				admin-login
//	@Accept			json
//	@Produce		json
//	@Param			param	body		domain.LoginReq	true	"登录参数"
//	@Success		200		{object}	web.Resp{data=domain.AdminUser}
//	@Router			/api/v1/admin/login [post]
func (h *UserHandler) AdminLogin(c *web.Context, req domain.LoginReq) error {
	req.IP = c.RealIP()
	resp, err := h.usecase.AdminLogin(c.Request().Context(), &req)
	if err != nil {
		return err
	}

	h.logger.With("header", c.Request().Header).With("host", c.Request().Host).Info("admin login", "username", resp.Username)
	if _, err := h.session.Save(c, consts.SessionName, c.Request().Host, resp); err != nil {
		return err
	}
	return c.Success(resp)
}

// AdminLogout 管理员登出
//
//	@Tags			Admin
//	@Summary		管理员登出
//	@Description	管理员登出
//	@ID				admin-logout
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	web.Resp{}
//	@Router			/api/v1/admin/logout [post]
func (h *UserHandler) AdminLogout(c *web.Context) error {
	if err := h.session.Del(c, consts.SessionName); err != nil {
		return err
	}
	return c.Success(nil)
}

// AdminProfile 管理员信息
//
//	@Tags			Admin
//	@Summary		管理员信息
//	@Description	管理员信息
//	@ID				admin-profile
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	web.Resp{data=domain.AdminUser}
//	@Router			/api/v1/admin/profile [get]
func (h *UserHandler) AdminProfile(c *web.Context) error {
	user := middleware.GetAdmin(c)
	return c.Success(user)
}

// List 获取用户列表
//
//	@Tags			User
//	@Summary		获取用户列表
//	@Description	获取用户列表
//	@ID				list-user
//	@Accept			json
//	@Produce		json
//	@Param			page	query		web.Pagination	true	"分页"
//	@Success		200		{object}	web.Resp{data=domain.ListUserResp}
//	@Router			/api/v1/user/list [get]
func (h *UserHandler) List(c *web.Context, req domain.ListReq) error {
	resp, err := h.usecase.List(c.Request().Context(), req)
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// LoginHistory 获取用户登录历史
//
//	@Tags			User
//	@Summary		获取用户登录历史
//	@Description	获取用户登录历史
//	@ID				login-history
//	@Accept			json
//	@Produce		json
//	@Param			page	query		web.Pagination	true	"分页"
//	@Success		200		{object}	web.Resp{data=domain.ListLoginHistoryResp}
//	@Router			/api/v1/user/login-history [get]
func (h *UserHandler) LoginHistory(c *web.Context) error {
	resp, err := h.usecase.LoginHistory(c.Request().Context(), c.Page())
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// Invite 获取用户邀请码
//
//	@Tags			User
//	@Summary		获取用户邀请码
//	@Description	获取用户邀请码
//	@ID				invite
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	web.Resp{data=domain.InviteResp}
//	@Router			/api/v1/user/invite [get]
func (h *UserHandler) Invite(c *web.Context) error {
	admin := middleware.GetAdmin(c)
	resp, err := h.usecase.Invite(c.Request().Context(), admin.ID.String())
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// Register 注册用户
//
//	@Tags			User
//	@Summary		注册用户
//	@Description	注册用户
//	@ID				register
//	@Accept			json
//	@Produce		json
//	@Param			param	body		domain.RegisterReq	true	"注册参数"
//	@Success		200		{object}	web.Resp{data=domain.User}
//	@Router			/api/v1/user/register [post]
func (h *UserHandler) Register(c *web.Context, req domain.RegisterReq) error {
	resp, err := h.usecase.Register(c.Request().Context(), &req)
	if err != nil {
		return err
	}

	return c.Success(resp)
}

// CreateAdmin 创建管理员
//
//	@Tags			Admin
//	@Summary		创建管理员
//	@Description	创建管理员
//	@ID				create-admin
//	@Accept			json
//	@Produce		json
//	@Param			param	body		domain.CreateAdminReq	true	"创建管理员参数"
//	@Success		200		{object}	web.Resp{data=domain.AdminUser}
//	@Router			/api/v1/admin/create [post]
func (h *UserHandler) CreateAdmin(c *web.Context, req domain.CreateAdminReq) error {
	user := middleware.GetAdmin(c)
	if user.Username != "admin" {
		return errcode.ErrPermission
	}
	resp, err := h.usecase.CreateAdmin(c.Request().Context(), &req)
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// AdminList 获取管理员用户列表
//
//	@Tags			Admin
//	@Summary		获取管理员用户列表
//	@Description	获取管理员用户列表
//	@ID				list-admin-user
//	@Accept			json
//	@Produce		json
//	@Param			page	query		web.Pagination	true	"分页"
//	@Success		200		{object}	web.Resp{data=domain.ListAdminUserResp}
//	@Router			/api/v1/admin/list [get]
func (h *UserHandler) AdminList(c *web.Context) error {
	resp, err := h.usecase.AdminList(c.Request().Context(), c.Page())
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// AdminLoginHistory 获取管理员登录历史
//
//	@Tags			Admin
//	@Summary		获取管理员登录历史
//	@Description	获取管理员登录历史
//	@ID				admin-login-history
//	@Accept			json
//	@Produce		json
//	@Param			page	query		web.Pagination	true	"分页"
//	@Success		200		{object}	web.Resp{data=domain.ListAdminLoginHistoryResp}
//	@Router			/api/v1/admin/login-history [get]
func (h *UserHandler) AdminLoginHistory(c *web.Context) error {
	resp, err := h.usecase.AdminLoginHistory(c.Request().Context(), c.Page())
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// ListRole 获取系统角色列表
//
//	@Tags			Admin
//	@Summary		获取角色列表
//	@Description	获取角色列表
//	@ID				list-role
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	web.Resp{data=[]domain.Role}
//	@Router			/api/v1/admin/role [get]
func (h *UserHandler) ListRole(c *web.Context) error {
	roles, err := h.usecase.ListRole(c.Request().Context())
	if err != nil {
		return err
	}
	return c.Success(roles)
}

// GrantRole 授权角色
//
//	@Tags			Admin
//	@Summary		授权角色
//	@Description	授权角色
//	@ID				grant-role
//	@Accept			json
//	@Produce		json
//	@Param			param	body		domain.GrantRoleReq	true	"授权角色参数"
//	@Success		200		{object}	web.Resp
//	@Router			/api/v1/admin/role [post]
func (h *UserHandler) GrantRole(c *web.Context, req domain.GrantRoleReq) error {
	if err := h.usecase.GrantRole(c.Request().Context(), &req); err != nil {
		return err
	}
	return c.Success(nil)
}

// GetSetting 获取系统设置
//
//	@Tags			Admin
//	@Summary		获取系统设置
//	@Description	获取系统设置
//	@ID				get-setting
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	web.Resp{data=domain.Setting}
//	@Router			/api/v1/admin/setting [get]
func (h *UserHandler) GetSetting(c *web.Context) error {
	resp, err := h.usecase.GetSetting(c.Request().Context())
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// UpdateSetting 更新系统设置
//
//	@Tags			Admin
//	@Summary		更新系统设置
//	@Description	更新为增量更新，只传需要更新的字段
//	@ID				update-setting
//	@Accept			json
//	@Produce		json
//	@Param			param	body		domain.UpdateSettingReq	true	"更新系统设置参数"
//	@Success		200		{object}	web.Resp{data=domain.Setting}
//	@Router			/api/v1/admin/setting [put]
func (h *UserHandler) UpdateSetting(c *web.Context, req domain.UpdateSettingReq) error {
	resp, err := h.usecase.UpdateSetting(c.Request().Context(), &req)
	if err != nil {
		return err
	}
	return c.Success(resp)
}

// OAuthSignUpOrIn 用户 OAuth 登录或注册
//
//	@Tags			User
//	@Summary		用户 OAuth 登录或注册
//	@Description	用户 OAuth 登录或注册
//	@ID				user-oauth-signup-or-in
//	@Accept			json
//	@Produce		json
//	@Param			req	query		domain.OAuthSignUpOrInReq	true	"param"
//	@Success		200	{object}	web.Resp{data=domain.OAuthURLResp}
//	@Router			/api/v1/user/oauth/signup-or-in [get]
func (h *UserHandler) OAuthSignUpOrIn(ctx *web.Context, req domain.OAuthSignUpOrInReq) error {
	h.logger.With("req", req).DebugContext(ctx.Request().Context(), "OAuthSignUpOrIn")
	s, err := h.usecase.GetSetting(ctx.Request().Context())
	if err != nil {
		return err
	}
	req.BaseURL = h.cfg.GetBaseURL(ctx.Request(), s)
	resp, err := h.usecase.OAuthSignUpOrIn(ctx.Request().Context(), &req)
	if err != nil {
		return err
	}
	return ctx.Success(resp)
}

// OAuthCallback 用户 OAuth 回调
//
//	@Tags			User
//	@Summary		用户 OAuth 回调
//	@Description	用户 OAuth 回调
//	@ID				user-oauth-callback
//	@Accept			json
//	@Produce		json
//	@Param			req	query		domain.OAuthCallbackReq	true	"param"
//	@Success		200	{object}	web.Resp{data=string}
//	@Router			/api/v1/user/oauth/callback [get]
func (h *UserHandler) OAuthCallback(ctx *web.Context, req domain.OAuthCallbackReq) error {
	return h.usecase.OAuthCallback(ctx, &req)
}

// Profile 获取用户信息
//
//	@Tags			User Manage
//	@Summary		获取用户信息
//	@Description	获取用户信息
//	@ID				user-profile
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	web.Resp{data=domain.User}
//	@Failure		401	{object}	web.Resp{}
//	@Router			/api/v1/user/profile [get]
func (h *UserHandler) Profile(ctx *web.Context) error {
	return ctx.Success(middleware.GetUser(ctx))
}

// UpdateProfile 更新用户信息
//
//	@Tags			User Manage
//	@Summary		更新用户信息
//	@Description	更新用户信息
//	@ID				user-update-profile
//	@Accept			json
//	@Produce		json
//	@Param			req	body		domain.ProfileUpdateReq	true	"param"
//	@Success		200	{object}	web.Resp{data=domain.User}
//	@Failure		401	{object}	web.Resp{}
//	@Router			/api/v1/user/profile [put]
func (h *UserHandler) UpdateProfile(ctx *web.Context, req domain.ProfileUpdateReq) error {
	req.UID = middleware.GetUser(ctx).ID
	user, err := h.usecase.ProfileUpdate(ctx.Request().Context(), &req)
	if err != nil {
		return err
	}
	return ctx.Success(user)
}

func (h *UserHandler) InitAdmin() error {
	return h.usecase.InitAdmin(context.Background())
}

// ExportCompletionData godoc
//
//	@Summary		导出补全数据
//	@Description	管理员导出所有补全相关数据
//	@Tags			admin
//	@Accept			json
//	@Produce		json
//	@Security		ApiKeyAuth
//	@Success		200	{object}	domain.ExportCompletionDataResp
//	@Failure		401	{object}	web.Resp{}
//	@Failure		500	{object}	web.Resp{}
//	@Router			/api/v1/admin/export-completion-data [get]
func (h *UserHandler) ExportCompletionData(c *web.Context) error {
	data, err := h.usecase.ExportCompletionData(c.Request().Context())
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, data)
}
