package service

import (
	"bytes"
	"context"
	"encoding/csv"
	"fmt"
	"strings"
	"time"

	"github.com/xuri/excelize/v2"

	"github.com/user-management-system/internal/auth"
	"github.com/user-management-system/internal/domain"
	"github.com/user-management-system/internal/repository"
)

const (
	ExportFormatCSV  = "csv"
	ExportFormatXLSX = "xlsx"
)

// Interfaces for dependency inversion (DIP) — service layer depends on these abstractions, not concrete types.
type exportUserRepository interface {
	List(ctx context.Context, offset, limit int) ([]*domain.User, int64, error)
	AdvancedSearch(ctx context.Context, filter *repository.AdvancedFilter) ([]*domain.User, int64, error)
	ExistsByUsername(ctx context.Context, username string) (bool, error)
	Create(ctx context.Context, user *domain.User) error
}

type exportRoleRepository interface {
	// Reserved for future use (role assignment during import)
}

// ExportUsersRequest defines the supported export filters and output options.
type ExportUsersRequest struct {
	Format  string
	Fields  []string
	Keyword string
	Status  *int
}

type exportColumn struct {
	Key    string
	Header string
	Value  func(*domain.User) string
}

var defaultExportColumns = []exportColumn{
	{Key: "id", Header: "ID", Value: func(u *domain.User) string { return fmt.Sprintf("%d", u.ID) }},
	{Key: "username", Header: "用户名", Value: func(u *domain.User) string { return u.Username }},
	{Key: "email", Header: "邮箱", Value: func(u *domain.User) string { return domain.DerefStr(u.Email) }},
	{Key: "phone", Header: "手机号", Value: func(u *domain.User) string { return domain.DerefStr(u.Phone) }},
	{Key: "nickname", Header: "昵称", Value: func(u *domain.User) string { return u.Nickname }},
	{Key: "avatar", Header: "头像", Value: func(u *domain.User) string { return u.Avatar }},
	{Key: "gender", Header: "性别", Value: func(u *domain.User) string { return genderLabel(u.Gender) }},
	{Key: "status", Header: "状态", Value: func(u *domain.User) string { return userStatusLabel(u.Status) }},
	{Key: "region", Header: "地区", Value: func(u *domain.User) string { return u.Region }},
	{Key: "bio", Header: "个人简介", Value: func(u *domain.User) string { return u.Bio }},
	{Key: "totp_enabled", Header: "TOTP已启用", Value: func(u *domain.User) string { return boolLabel(u.TOTPEnabled) }},
	{Key: "last_login_time", Header: "最后登录时间", Value: func(u *domain.User) string { return timeLabel(u.LastLoginTime) }},
	{Key: "last_login_ip", Header: "最后登录IP", Value: func(u *domain.User) string { return u.LastLoginIP }},
	{Key: "created_at", Header: "注册时间", Value: func(u *domain.User) string { return u.CreatedAt.Format("2006-01-02 15:04:05") }},
}

// ExportService 用户数据导入导出服务
type ExportService struct {
	userRepo exportUserRepository
	roleRepo exportRoleRepository
}

// NewExportService 创建导入导出服务
func NewExportService(
	userRepo exportUserRepository,
	roleRepo exportRoleRepository,
) *ExportService {
	return &ExportService{
		userRepo: userRepo,
		roleRepo: roleRepo,
	}
}

// ExportUsers exports users as CSV or XLSX.
func (s *ExportService) ExportUsers(ctx context.Context, req *ExportUsersRequest) ([]byte, string, string, error) {
	if req == nil {
		req = &ExportUsersRequest{}
	}

	format, err := normalizeExportFormat(req.Format)
	if err != nil {
		return nil, "", "", err
	}

	columns, err := resolveExportColumns(req.Fields)
	if err != nil {
		return nil, "", "", err
	}

	users, err := s.listUsersForExport(ctx, req)
	if err != nil {
		return nil, "", "", err
	}

	filename := fmt.Sprintf("users_%s.%s", time.Now().Format("20060102_150405"), format)
	switch format {
	case ExportFormatCSV:
		data, err := buildCSVExport(columns, users)
		if err != nil {
			return nil, "", "", err
		}
		return data, filename, "text/csv; charset=utf-8", nil
	case ExportFormatXLSX:
		data, err := buildXLSXExport(columns, users)
		if err != nil {
			return nil, "", "", err
		}
		return data, filename, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", nil
	default:
		return nil, "", "", fmt.Errorf("不支持的导出格式: %s", req.Format)
	}
}

// ExportUsersCSV keeps backward compatibility for callers that still expect CSV-only export.
func (s *ExportService) ExportUsersCSV(ctx context.Context) ([]byte, string, error) {
	data, filename, _, err := s.ExportUsers(ctx, &ExportUsersRequest{Format: ExportFormatCSV})
	return data, filename, err
}

// ExportUsersXLSX exports users as Excel.
func (s *ExportService) ExportUsersXLSX(ctx context.Context) ([]byte, string, error) {
	data, filename, _, err := s.ExportUsers(ctx, &ExportUsersRequest{Format: ExportFormatXLSX})
	return data, filename, err
}

func (s *ExportService) listUsersForExport(ctx context.Context, req *ExportUsersRequest) ([]*domain.User, error) {
	var allUsers []*domain.User
	offset := 0
	batchSize := 500

	for {
		var (
			users []*domain.User
			total int64
			err   error
		)

		if req.Keyword != "" || req.Status != nil {
			filter := &repository.AdvancedFilter{
				Keyword:   req.Keyword,
				Status:    -1,
				SortBy:    "created_at",
				SortOrder: "desc",
				Offset:    offset,
				Limit:     batchSize,
			}
			if req.Status != nil {
				filter.Status = *req.Status
			}
			users, total, err = s.userRepo.AdvancedSearch(ctx, filter)
			if err != nil {
				return nil, fmt.Errorf("查询用户失败: %w", err)
			}
			allUsers = append(allUsers, users...)
			offset += len(users)
			if offset >= int(total) || len(users) == 0 {
				break
			}
			continue
		}

		users, _, err = s.userRepo.List(ctx, offset, batchSize)
		if err != nil {
			return nil, fmt.Errorf("查询用户失败: %w", err)
		}
		allUsers = append(allUsers, users...)
		if len(users) < batchSize {
			break
		}
		offset += batchSize
	}

	return allUsers, nil
}

// ImportUsers imports users from CSV or XLSX.
func (s *ExportService) ImportUsers(ctx context.Context, data []byte, format string) (successCount, failCount int, errs []string) {
	normalized, err := normalizeExportFormat(format)
	if err != nil {
		return 0, 0, []string{err.Error()}
	}

	var records [][]string
	switch normalized {
	case ExportFormatCSV:
		records, err = parseCSVRecords(data)
	case ExportFormatXLSX:
		records, err = parseXLSXRecords(data)
	default:
		err = fmt.Errorf("不支持的导入格式: %s", format)
	}
	if err != nil {
		return 0, 0, []string{err.Error()}
	}

	return s.importUsersRecords(ctx, records)
}

// ImportUsersCSV keeps backward compatibility for callers that still upload CSV.
func (s *ExportService) ImportUsersCSV(ctx context.Context, data []byte) (successCount, failCount int, errs []string) {
	return s.ImportUsers(ctx, data, ExportFormatCSV)
}

// ImportUsersXLSX imports users from Excel.
func (s *ExportService) ImportUsersXLSX(ctx context.Context, data []byte) (successCount, failCount int, errs []string) {
	return s.ImportUsers(ctx, data, ExportFormatXLSX)
}

func (s *ExportService) importUsersRecords(ctx context.Context, records [][]string) (successCount, failCount int, errs []string) {
	if len(records) < 2 {
		return 0, 0, []string{"导入文件为空或没有数据行"}
	}

	headers := records[0]
	colIdx := buildColIndex(headers)
	getCol := func(row []string, name string) string {
		idx, ok := colIdx[name]
		if !ok || idx >= len(row) {
			return ""
		}
		return strings.TrimSpace(row[idx])
	}

	for i, row := range records[1:] {
		lineNum := i + 2
		username := getCol(row, "用户名")
		password := getCol(row, "密码")

		if username == "" || password == "" {
			failCount++
			errs = append(errs, fmt.Sprintf("第%d行：用户名和密码不能为空", lineNum))
			continue
		}

		exists, err := s.userRepo.ExistsByUsername(ctx, username)
		if err != nil {
			failCount++
			errs = append(errs, fmt.Sprintf("第%d行：检查用户名失败: %v", lineNum, err))
			continue
		}
		if exists {
			failCount++
			errs = append(errs, fmt.Sprintf("第%d行：用户名 '%s' 已存在", lineNum, username))
			continue
		}

		hashedPwd, err := hashPassword(password)
		if err != nil {
			failCount++
			errs = append(errs, fmt.Sprintf("第%d行：密码加密失败: %v", lineNum, err))
			continue
		}

		user := &domain.User{
			Username: username,
			Email:    domain.StrPtr(getCol(row, "邮箱")),
			Phone:    domain.StrPtr(getCol(row, "手机号")),
			Nickname: getCol(row, "昵称"),
			Password: hashedPwd,
			Region:   getCol(row, "地区"),
			Bio:      getCol(row, "个人简介"),
			Status:   domain.UserStatusActive,
		}

		if err := s.userRepo.Create(ctx, user); err != nil {
			failCount++
			errs = append(errs, fmt.Sprintf("第%d行：创建用户失败: %v", lineNum, err))
			continue
		}
		successCount++
	}

	return successCount, failCount, errs
}

// GetImportTemplate keeps backward compatibility for callers that still expect CSV templates.
func (s *ExportService) GetImportTemplate() ([]byte, string) {
	data, filename, _, _ := s.GetImportTemplateByFormat(ExportFormatCSV)
	return data, filename
}

// GetImportTemplateByFormat returns a CSV or XLSX template for imports.
func (s *ExportService) GetImportTemplateByFormat(format string) ([]byte, string, string, error) {
	normalized, err := normalizeExportFormat(format)
	if err != nil {
		return nil, "", "", err
	}

	headers := []string{"用户名", "密码", "邮箱", "手机号", "昵称", "性别", "地区", "个人简介"}
	rows := [][]string{{
		"john_doe", "Password123!", "john@example.com", "13800138000",
		"约翰", "男", "北京", "这是个人简介",
	}}

	switch normalized {
	case ExportFormatCSV:
		data, err := buildCSVRecords(headers, rows)
		if err != nil {
			return nil, "", "", err
		}
		return data, "user_import_template.csv", "text/csv; charset=utf-8", nil
	case ExportFormatXLSX:
		data, err := buildXLSXRecords(headers, rows)
		if err != nil {
			return nil, "", "", err
		}
		return data, "user_import_template.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", nil
	default:
		return nil, "", "", fmt.Errorf("不支持的模板格式: %s", format)
	}
}

func normalizeExportFormat(format string) (string, error) {
	normalized := strings.ToLower(strings.TrimSpace(format))
	if normalized == "" {
		normalized = ExportFormatCSV
	}
	switch normalized {
	case ExportFormatCSV, ExportFormatXLSX:
		return normalized, nil
	default:
		return "", fmt.Errorf("不支持的格式: %s", format)
	}
}

func resolveExportColumns(fields []string) ([]exportColumn, error) {
	if len(fields) == 0 {
		return defaultExportColumns, nil
	}

	columnMap := make(map[string]exportColumn, len(defaultExportColumns))
	for _, col := range defaultExportColumns {
		columnMap[col.Key] = col
	}

	selected := make([]exportColumn, 0, len(fields))
	seen := make(map[string]struct{}, len(fields))
	for _, field := range fields {
		key := strings.ToLower(strings.TrimSpace(field))
		if key == "" {
			continue
		}
		if _, ok := seen[key]; ok {
			continue
		}
		col, ok := columnMap[key]
		if !ok {
			return nil, fmt.Errorf("不支持的导出字段: %s", field)
		}
		selected = append(selected, col)
		seen[key] = struct{}{}
	}

	if len(selected) == 0 {
		return defaultExportColumns, nil
	}

	return selected, nil
}

func buildCSVExport(columns []exportColumn, users []*domain.User) ([]byte, error) {
	headers := make([]string, 0, len(columns))
	rows := make([][]string, 0, len(users))
	for _, col := range columns {
		headers = append(headers, col.Header)
	}
	for _, u := range users {
		row := make([]string, 0, len(columns))
		for _, col := range columns {
			row = append(row, col.Value(u))
		}
		rows = append(rows, row)
	}
	return buildCSVRecords(headers, rows)
}

func buildCSVRecords(headers []string, rows [][]string) ([]byte, error) {
	var buf bytes.Buffer
	buf.Write([]byte{0xEF, 0xBB, 0xBF})
	writer := csv.NewWriter(&buf)

	if err := writer.Write(headers); err != nil {
		return nil, fmt.Errorf("写CSV表头失败: %w", err)
	}
	for _, row := range rows {
		if err := writer.Write(row); err != nil {
			return nil, fmt.Errorf("写CSV行失败: %w", err)
		}
	}
	writer.Flush()
	if err := writer.Error(); err != nil {
		return nil, fmt.Errorf("CSV Flush 失败: %w", err)
	}
	return buf.Bytes(), nil
}

func buildXLSXExport(columns []exportColumn, users []*domain.User) ([]byte, error) {
	headers := make([]string, 0, len(columns))
	rows := make([][]string, 0, len(users))
	for _, col := range columns {
		headers = append(headers, col.Header)
	}
	for _, u := range users {
		row := make([]string, 0, len(columns))
		for _, col := range columns {
			row = append(row, col.Value(u))
		}
		rows = append(rows, row)
	}
	return buildXLSXRecords(headers, rows)
}

func buildXLSXRecords(headers []string, rows [][]string) ([]byte, error) {
	file := excelize.NewFile()
	defer file.Close()

	sheet := file.GetSheetName(file.GetActiveSheetIndex())
	if sheet == "" {
		sheet = "Sheet1"
	}

	for idx, header := range headers {
		cell, err := excelize.CoordinatesToCellName(idx+1, 1)
		if err != nil {
			return nil, fmt.Errorf("生成表头单元格失败: %w", err)
		}
		if err := file.SetCellValue(sheet, cell, header); err != nil {
			return nil, fmt.Errorf("写入表头失败: %w", err)
		}
	}

	for rowIdx, row := range rows {
		for colIdx, value := range row {
			cell, err := excelize.CoordinatesToCellName(colIdx+1, rowIdx+2)
			if err != nil {
				return nil, fmt.Errorf("生成数据单元格失败: %w", err)
			}
			if err := file.SetCellValue(sheet, cell, value); err != nil {
				return nil, fmt.Errorf("写入单元格失败: %w", err)
			}
		}
	}

	var buf bytes.Buffer
	if _, err := file.WriteTo(&buf); err != nil {
		return nil, fmt.Errorf("生成Excel失败: %w", err)
	}
	return buf.Bytes(), nil
}

func parseCSVRecords(data []byte) ([][]string, error) {
	if len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF {
		data = data[3:]
	}

	reader := csv.NewReader(bytes.NewReader(data))
	records, err := reader.ReadAll()
	if err != nil {
		return nil, fmt.Errorf("CSV 解析失败: %w", err)
	}
	return records, nil
}

func parseXLSXRecords(data []byte) ([][]string, error) {
	file, err := excelize.OpenReader(bytes.NewReader(data))
	if err != nil {
		return nil, fmt.Errorf("excel parse failed: %w", err)
	}
	defer file.Close()

	sheets := file.GetSheetList()
	if len(sheets) == 0 {
		return nil, fmt.Errorf("excel file has no available sheets")
	}

	rows, err := file.GetRows(sheets[0])
	if err != nil {
		return nil, fmt.Errorf("读取Excel行失败: %w", err)
	}
	return rows, nil
}

// ---- 辅助函数 ----

func genderLabel(g domain.Gender) string {
	switch g {
	case domain.GenderMale:
		return "男"
	case domain.GenderFemale:
		return "女"
	default:
		return "未知"
	}
}

func userStatusLabel(s domain.UserStatus) string {
	switch s {
	case domain.UserStatusActive:
		return "已激活"
	case domain.UserStatusInactive:
		return "未激活"
	case domain.UserStatusLocked:
		return "已锁定"
	case domain.UserStatusDisabled:
		return "已禁用"
	default:
		return "未知"
	}
}

func boolLabel(b bool) string {
	if b {
		return "是"
	}
	return "否"
}

func timeLabel(t *time.Time) string {
	if t == nil {
		return ""
	}
	return t.Format("2006-01-02 15:04:05")
}

// buildColIndex 将表头列名映射到列索引
func buildColIndex(headers []string) map[string]int {
	idx := make(map[string]int, len(headers))
	for i, h := range headers {
		idx[h] = i
	}
	return idx
}

// hashPassword hashes imported passwords with the primary runtime algorithm.
func hashPassword(password string) (string, error) {
	return auth.HashPassword(password)
}
