package pdfrender

import (
	"fmt"
	"strings"
)

// GenerateContinuationTemplateName creates a continuation template name
// Example: "page_1.html" + pageNum 2 -> "page_1.2.html"
// Note: All continuation pages (2, 3, 4, ...) use the same template name: page_1.2.html
func GenerateContinuationTemplateName(originalTemplate string, pageNum int) string {
	if pageNum == 1 {
		return originalTemplate
	}

	// All continuation pages use .2 template (page_1.2, page_2.2, etc.)
	// NOT page_1.3, page_1.4, etc.

	// New format: "page_1.html" -> "page_1.2.html"
	// Pattern: page_N.html where N is a number
	ext := ".html"
	base := strings.TrimSuffix(originalTemplate, ext)

	// Check if it's already a continuation (has .2 in it)
	if strings.Contains(base, ".2") {
		return originalTemplate
	}

	// Append .2 before .html
	return fmt.Sprintf("%s.2%s", base, ext)
}

// ExtractBaseTemplateName extracts the base template name from a continuation template
// Example: "page_1.2.html" -> "page_1.html"
func ExtractBaseTemplateName(templateName string) string {
	// New format: "page_1.2.html" or "page_1.10.html" -> "page_1.html"
	// Pattern: ends with .N.html where N is any number
	ext := ".html"
	if !strings.HasSuffix(templateName, ext) {
		return templateName
	}

	// Remove .html
	base := strings.TrimSuffix(templateName, ext)

	// Find the last dot
	lastDotIdx := strings.LastIndex(base, ".")
	if lastDotIdx == -1 {
		return templateName // No dot found, not a continuation
	}

	// Check if everything after the last dot is a number
	numPart := base[lastDotIdx+1:]
	if len(numPart) == 0 {
		return templateName
	}

	for _, c := range numPart {
		if c < '0' || c > '9' {
			return templateName // Not a number, not a continuation template
		}
	}

	// It's a continuation template, return the base name
	return base[:lastDotIdx] + ext
}

// SliceList slices a list based on start index and max items
// Returns the sliced list and whether there are more items
func SliceList[T any](fullList []T, startIndex, maxItems int) ([]T, bool) {
	totalCount := len(fullList)
	endIndex := startIndex + maxItems

	if startIndex >= totalCount {
		return []T{}, false
	}

	if endIndex > totalCount {
		endIndex = totalCount
	}

	return fullList[startIndex:endIndex], endIndex < totalCount
}

// PageDistribution represents how data is distributed across pages
type PageDistribution struct {
	TemplateName string                 // Template to use for this page
	PageNumber   int                    // Page number (1-indexed)
	Data         map[string]interface{} // Block name -> data slice
}

// Paginator handles pagination of lists according to template metadata
type Paginator struct {
	templateSet TemplateSet
}

// NewPaginator creates a new paginator with template metadata
func NewPaginator(templateSet TemplateSet) *Paginator {
	return &Paginator{
		templateSet: templateSet,
	}
}

// PaginateMultiList is a unified pagination function that handles multiple list types
// It replaces PaginateSkills, PaginateSpells, and PaginatePage2PlayLists
// dataMap keys: "skills", "weapons", "spells", "equipment", "magicItems"
func (p *Paginator) PaginateMultiList(dataMap map[string]interface{}, templateName string) ([]PageDistribution, error) {
	template := p.findTemplate(templateName)
	if template == nil {
		return nil, fmt.Errorf("template not found: %s", templateName)
	}

	// Build filtered lists for each unique list type + filter combination
	type listTracker struct {
		items      interface{}
		currentIdx int
		totalCount int
	}

	// Track by "listType:filter" to avoid duplicates
	listTrackers := make(map[string]*listTracker)

	// Scan both base template and continuation template to find all unique listType:filter combinations
	templatesToScan := []*TemplateMetadata{template}
	continuationName := GenerateContinuationTemplateName(templateName, 2)
	if contTemplate := p.findTemplate(continuationName); contTemplate != nil {
		templatesToScan = append(templatesToScan, contTemplate)
	}

	// First pass: create filtered lists for each unique listType+filter combination
	for _, tmpl := range templatesToScan {
		for _, block := range tmpl.Blocks {
			// Get the source data based on block's ListType
			var sourceData interface{}
			switch block.ListType {
			case "skills":
				sourceData = dataMap["skills"]
			case "weapons":
				sourceData = dataMap["weapons"]
			case "spells":
				sourceData = dataMap["spells"]
			case "equipment":
				sourceData = dataMap["equipment"]
			case "magicItems":
				sourceData = dataMap["magicItems"]
			default:
				continue
			}

			if sourceData == nil {
				continue
			}

			// Create unique key for this list type + filter combination
			trackerKey := block.ListType
			if block.Filter != "" {
				trackerKey += ":" + block.Filter
			}

			// Only create tracker once per unique combination
			if _, exists := listTrackers[trackerKey]; !exists {
				// Apply filter if specified
				filteredItems := p.applyFilter(sourceData, block.Filter)
				itemCount := p.getItemCount(filteredItems)

				if itemCount > 0 {
					listTrackers[trackerKey] = &listTracker{
						items:      filteredItems,
						currentIdx: 0,
						totalCount: itemCount,
					}
				}
			}
		}
	}

	// If all lists are empty, return empty result
	if len(listTrackers) == 0 {
		return []PageDistribution{}, nil
	}

	// Generate pages until all items are distributed
	distributions := []PageDistribution{}
	pageNum := 1

	for {
		// Check if there are any remaining items
		hasRemainingItems := false
		for _, tracker := range listTrackers {
			if tracker.currentIdx < tracker.totalCount {
				hasRemainingItems = true
				break
			}
		}

		if !hasRemainingItems {
			break
		}

		// Determine template name for this page
		pageTemplateName := GenerateContinuationTemplateName(templateName, pageNum)

		// Load the correct template metadata for this page
		// Fall back to base template if continuation template doesn't exist
		currentTemplate := p.findTemplate(pageTemplateName)
		if currentTemplate == nil {
			currentTemplate = template
		}

		// Create page data
		pageData := make(map[string]interface{})

		// Distribute items to each block for this page
		for _, block := range currentTemplate.Blocks {
			// Get tracker for this block's list type + filter
			trackerKey := block.ListType
			if block.Filter != "" {
				trackerKey += ":" + block.Filter
			}

			tracker, exists := listTrackers[trackerKey]
			if !exists {
				// Skip blocks with NOEMPTY flag when they have no data
				if block.NoEmpty {
					continue
				}
				// Block has no data, fill with empty items up to MAX
				pageData[block.Name] = p.createEmptySliceWithCapacity(block.ListType, block.MaxItems)
				continue
			}

			// Calculate how many items to take for this block
			itemsToTake := block.MaxItems
			remaining := tracker.totalCount - tracker.currentIdx
			if itemsToTake > remaining {
				itemsToTake = remaining
			}

			// Skip NOEMPTY blocks when no items remain
			if block.NoEmpty && itemsToTake == 0 {
				continue
			}
			// For regular blocks with no items remaining, fill with empty rows
			if itemsToTake == 0 {
				pageData[block.Name] = p.createEmptySliceWithCapacity(block.ListType, block.MaxItems)
				continue
			}
			// Extract slice for this block
			blockItems := p.extractSlice(tracker.items, tracker.currentIdx, itemsToTake)

			// Always fill to capacity (both normal and NOEMPTY blocks get filled when they have items)
			blockItems = p.fillSliceToCapacity(blockItems, block.MaxItems)

			pageData[block.Name] = blockItems
			tracker.currentIdx += itemsToTake
		}

		// Template name was already determined at the start of the loop
		distributions = append(distributions, PageDistribution{
			TemplateName: pageTemplateName,
			PageNumber:   pageNum,
			Data:         pageData,
		})

		pageNum++
	}

	return distributions, nil
}

// applyFilter filters a list based on filter criteria
func (p *Paginator) applyFilter(items interface{}, filter string) interface{} {
	if filter == "" {
		return items
	}

	switch v := items.(type) {
	case []SkillViewModel:
		filtered := []SkillViewModel{}
		for _, skill := range v {
			include := false
			switch filter {
			case "learned":
				include = (skill.IsLearned || skill.Category == "Waffenfertigkeit") && skill.Category != "Sprache"
			case "unlearned":
				include = !skill.IsLearned && skill.Category != "Sprache"
			case "language", "languages":
				include = skill.Category == "Sprache"
			default:
				include = true
			}
			if include {
				filtered = append(filtered, skill)
			}
		}
		return filtered
	default:
		// No filtering for other types
		return items
	}
}

// getItemCount returns the count of items in a list
func (p *Paginator) getItemCount(items interface{}) int {
	switch v := items.(type) {
	case []SkillViewModel:
		return len(v)
	case []WeaponViewModel:
		return len(v)
	case []SpellViewModel:
		return len(v)
	case []EquipmentViewModel:
		return len(v)
	case []MagicItemViewModel:
		return len(v)
	default:
		return 0
	}
}

// PaginateSkills splits skills across multiple pages according to template capacity
func (p *Paginator) PaginateSkills(skills []SkillViewModel, templateName string, filter string) ([]PageDistribution, error) {
	template := p.findTemplate(templateName)
	if template == nil {
		return nil, fmt.Errorf("template not found: %s", templateName)
	}

	blocks := p.getBlocksForType(template, "skills", filter)
	if len(blocks) == 0 {
		return []PageDistribution{}, nil
	}

	return p.paginateList(skills, blocks, templateName, "skills")
}

// PaginateWeapons splits weapons across multiple pages
func (p *Paginator) PaginateWeapons(weapons []WeaponViewModel, templateName string) ([]PageDistribution, error) {
	template := p.findTemplate(templateName)
	if template == nil {
		return nil, fmt.Errorf("template not found: %s", templateName)
	}

	blocks := p.getBlocksForType(template, "weapons", "")
	if len(blocks) == 0 {
		return []PageDistribution{}, nil
	}

	return p.paginateList(weapons, blocks, templateName, "weapons")
}

// PaginateSpells splits spells across multiple pages and columns
func (p *Paginator) PaginateSpells(spells []SpellViewModel, templateName string) ([]PageDistribution, error) {
	template := p.findTemplate(templateName)
	if template == nil {
		return nil, fmt.Errorf("template not found: %s", templateName)
	}

	blocks := p.getBlocksForType(template, "spells", "")
	if len(blocks) == 0 {
		return []PageDistribution{}, nil
	}

	return p.paginateList(spells, blocks, templateName, "spells")
}

// PaginateEquipment splits equipment across multiple pages
func (p *Paginator) PaginateEquipment(equipment []EquipmentViewModel, templateName string) ([]PageDistribution, error) {
	template := p.findTemplate(templateName)
	if template == nil {
		return nil, fmt.Errorf("template not found: %s", templateName)
	}

	blocks := p.getBlocksForType(template, "equipment", "")
	if len(blocks) == 0 {
		return []PageDistribution{}, nil
	}

	return p.paginateList(equipment, blocks, templateName, "equipment")
}

// paginateList is the core pagination algorithm
func (p *Paginator) paginateList(items interface{}, blocks []BlockMetadata, templateName string, listType string) ([]PageDistribution, error) {
	// Convert items to slice length
	itemCount := 0
	switch v := items.(type) {
	case []SkillViewModel:
		itemCount = len(v)
	case []WeaponViewModel:
		itemCount = len(v)
	case []SpellViewModel:
		itemCount = len(v)
	case []EquipmentViewModel:
		itemCount = len(v)
	default:
		return nil, fmt.Errorf("unsupported item type")
	}

	if itemCount == 0 {
		return []PageDistribution{}, nil
	}

	// Calculate total capacity per page
	capacityPerPage := 0
	for _, block := range blocks {
		capacityPerPage += block.MaxItems
	}

	if capacityPerPage == 0 {
		return nil, fmt.Errorf("template has no capacity for list type: %s", listType)
	}

	// Calculate number of pages needed
	pageCount := (itemCount + capacityPerPage - 1) / capacityPerPage

	distributions := make([]PageDistribution, 0, pageCount)
	currentIndex := 0

	for pageNum := 1; pageNum <= pageCount; pageNum++ {
		pageData := make(map[string]interface{})

		// Distribute items across blocks in this page
		for _, block := range blocks {
			if currentIndex >= itemCount {
				// No more items, add empty slice
				pageData[block.Name] = p.createEmptySlice(listType)
				continue
			}

			// Calculate how many items to put in this block
			itemsToTake := block.MaxItems
			if currentIndex+itemsToTake > itemCount {
				itemsToTake = itemCount - currentIndex
			}

			// Extract slice for this block
			blockItems := p.extractSlice(items, currentIndex, itemsToTake)
			pageData[block.Name] = blockItems
			currentIndex += itemsToTake
		}

		// Determine template name - use continuation naming for pages 2+
		pageTemplateName := GenerateContinuationTemplateName(templateName, pageNum)

		distributions = append(distributions, PageDistribution{
			TemplateName: pageTemplateName,
			PageNumber:   pageNum,
			Data:         pageData,
		})
	}

	return distributions, nil
}

// findTemplate finds a template by name
func (p *Paginator) findTemplate(templateName string) *TemplateMetadata {
	for _, tmpl := range p.templateSet.Templates {
		if tmpl.Metadata.Name == templateName {
			return &tmpl.Metadata
		}
	}
	return nil
}

// getBlocksForType returns all blocks matching the list type and filter
func (p *Paginator) getBlocksForType(template *TemplateMetadata, listType string, filter string) []BlockMetadata {
	var blocks []BlockMetadata
	for _, block := range template.Blocks {
		if block.ListType == listType {
			if filter == "" || block.Filter == filter {
				blocks = append(blocks, block)
			}
		}
	}
	return blocks
}

// extractSlice extracts a slice of items based on type
func (p *Paginator) extractSlice(items interface{}, start, count int) interface{} {
	switch v := items.(type) {
	case []SkillViewModel:
		end := start + count
		if end > len(v) {
			end = len(v)
		}
		return v[start:end]
	case []WeaponViewModel:
		end := start + count
		if end > len(v) {
			end = len(v)
		}
		return v[start:end]
	case []SpellViewModel:
		end := start + count
		if end > len(v) {
			end = len(v)
		}
		return v[start:end]
	case []EquipmentViewModel:
		end := start + count
		if end > len(v) {
			end = len(v)
		}
		return v[start:end]
	case []MagicItemViewModel:
		end := start + count
		if end > len(v) {
			end = len(v)
		}
		return v[start:end]
	}
	return nil
}

// createEmptySlice creates an empty slice of the appropriate type
func (p *Paginator) createEmptySlice(listType string) interface{} {
	switch listType {
	case "skills":
		return []SkillViewModel{}
	case "weapons":
		return []WeaponViewModel{}
	case "spells":
		return []SpellViewModel{}
	case "equipment":
		return []EquipmentViewModel{}
	case "magicItems":
		return []MagicItemViewModel{}
	default:
		return []interface{}{}
	}
}

// createEmptySliceWithCapacity creates an empty slice filled to capacity with zero values
func (p *Paginator) createEmptySliceWithCapacity(listType string, capacity int) interface{} {
	switch listType {
	case "skills":
		return FillToCapacity([]SkillViewModel{}, capacity)
	case "weapons":
		return FillToCapacity([]WeaponViewModel{}, capacity)
	case "spells":
		return FillToCapacity([]SpellViewModel{}, capacity)
	case "equipment":
		return FillToCapacity([]EquipmentViewModel{}, capacity)
	case "magicItems":
		return FillToCapacity([]MagicItemViewModel{}, capacity)
	default:
		return []interface{}{}
	}
}

// fillSliceToCapacity fills an existing slice to the specified capacity
func (p *Paginator) fillSliceToCapacity(items interface{}, capacity int) interface{} {
	switch v := items.(type) {
	case []SkillViewModel:
		return FillToCapacity(v, capacity)
	case []WeaponViewModel:
		return FillToCapacity(v, capacity)
	case []SpellViewModel:
		return FillToCapacity(v, capacity)
	case []EquipmentViewModel:
		return FillToCapacity(v, capacity)
	case []MagicItemViewModel:
		return FillToCapacity(v, capacity)
	default:
		return items
	}
}

// CalculatePagesNeeded calculates how many pages are needed for given data
func (p *Paginator) CalculatePagesNeeded(templateName string, listType string, itemCount int) (int, error) {
	template := p.findTemplate(templateName)
	if template == nil {
		return 0, fmt.Errorf("template not found: %s", templateName)
	}

	blocks := p.getBlocksForType(template, listType, "")
	if len(blocks) == 0 {
		return 0, nil
	}

	capacityPerPage := 0
	for _, block := range blocks {
		capacityPerPage += block.MaxItems
	}

	if capacityPerPage == 0 {
		return 0, fmt.Errorf("template has no capacity for list type: %s", listType)
	}

	return (itemCount + capacityPerPage - 1) / capacityPerPage, nil
}

// PaginatePage2PlayLists handles pagination for page2_play.html which has both skills and weapons
// Skills and weapons overflow together - if either overflows, create continuation pages with remaining items from both
func (p *Paginator) PaginatePage2PlayLists(skills []SkillViewModel, weapons []WeaponViewModel, templateName string) ([]PageDistribution, error) {
	template := p.findTemplate(templateName)
	if template == nil {
		return nil, fmt.Errorf("template not found: %s", templateName)
	}

	// Get capacities for each block type
	learnedCap := GetBlockCapacity(&p.templateSet, templateName, "skills_learned")
	unlearnedCap := GetBlockCapacity(&p.templateSet, templateName, "skills_unlearned")
	languageCap := GetBlockCapacity(&p.templateSet, templateName, "skills_languages")
	weaponsCap := GetBlockCapacity(&p.templateSet, templateName, "weapons_main")

	// Filter skills into categories
	var learnedSkills, unlearnedSkills, languageSkills []SkillViewModel
	for _, skill := range skills {
		if skill.Category == "Sprache" {
			languageSkills = append(languageSkills, skill)
		} else if skill.IsLearned {
			learnedSkills = append(learnedSkills, skill)
		} else {
			unlearnedSkills = append(unlearnedSkills, skill)
		}
	}

	// Track current position in each list
	learnedIdx := 0
	unlearnedIdx := 0
	languageIdx := 0
	weaponsIdx := 0

	distributions := []PageDistribution{}
	pageNum := 1

	// Continue creating pages while there are remaining items in any list
	for learnedIdx < len(learnedSkills) || unlearnedIdx < len(unlearnedSkills) ||
		languageIdx < len(languageSkills) || weaponsIdx < len(weapons) {

		pageData := make(map[string]interface{})

		// Add learned skills for this page
		learnedEnd := learnedIdx + learnedCap
		if learnedEnd > len(learnedSkills) {
			learnedEnd = len(learnedSkills)
		}
		pageData["skills_learned"] = learnedSkills[learnedIdx:learnedEnd]
		learnedIdx = learnedEnd

		// Add unlearned skills for this page
		unlearnedEnd := unlearnedIdx + unlearnedCap
		if unlearnedEnd > len(unlearnedSkills) {
			unlearnedEnd = len(unlearnedSkills)
		}
		pageData["skills_unlearned"] = unlearnedSkills[unlearnedIdx:unlearnedEnd]
		unlearnedIdx = unlearnedEnd

		// Add language skills for this page
		languageEnd := languageIdx + languageCap
		if languageEnd > len(languageSkills) {
			languageEnd = len(languageSkills)
		}
		pageData["skills_languages"] = languageSkills[languageIdx:languageEnd]
		languageIdx = languageEnd

		// Add weapons for this page
		weaponsEnd := weaponsIdx + weaponsCap
		if weaponsEnd > len(weapons) {
			weaponsEnd = len(weapons)
		}
		pageData["weapons_main"] = weapons[weaponsIdx:weaponsEnd]
		weaponsIdx = weaponsEnd

		// Create page distribution
		pageTemplateName := GenerateContinuationTemplateName(templateName, pageNum)
		distributions = append(distributions, PageDistribution{
			TemplateName: pageTemplateName,
			PageNumber:   pageNum,
			Data:         pageData,
		})

		pageNum++
	}

	return distributions, nil
}
