package linguistics

import (
	"strings"
	"unicode"
)

// PoeticAnalyzer analyzes the poetic structure of text
type PoeticAnalyzer struct{}

// NewPoeticAnalyzer creates a new PoeticAnalyzer
func NewPoeticAnalyzer() *PoeticAnalyzer {
	return &PoeticAnalyzer{}
}

// Analyse analyzes the poetic structure of text and returns metrics
func (a *PoeticAnalyzer) Analyse(text Text) (PoeticMetrics, error) {
	// This is a simplified implementation
	// In a real-world scenario, you would use a more sophisticated approach

	content := text.Body

	// Split into lines
	lines := strings.Split(content, "\n")

	// Count non-empty lines
	var nonEmptyLines []string
	for _, line := range lines {
		if strings.TrimSpace(line) != "" {
			nonEmptyLines = append(nonEmptyLines, line)
		}
	}

	// Count stanzas (groups of lines separated by blank lines)
	stanzaCount := 0
	inStanza := false
	for _, line := range lines {
		if strings.TrimSpace(line) == "" {
			if inStanza {
				inStanza = false
			}
		} else {
			if !inStanza {
				inStanza = true
				stanzaCount++
			}
		}
	}

	// Ensure at least one stanza if there are lines
	if len(nonEmptyLines) > 0 && stanzaCount == 0 {
		stanzaCount = 1
	}

	// Detect rhyme scheme
	rhymeScheme := detectRhymeScheme(nonEmptyLines)

	// Detect meter type
	meterType := detectMeterType(nonEmptyLines)

	// Determine structure
	structure := determineStructure(stanzaCount, len(nonEmptyLines), rhymeScheme, meterType)

	metrics := PoeticMetrics{
		RhymeScheme: rhymeScheme,
		MeterType:   meterType,
		StanzaCount: stanzaCount,
		LineCount:   len(nonEmptyLines),
		Structure:   structure,
	}

	return metrics, nil
}

// detectRhymeScheme detects the rhyme scheme of a poem
func detectRhymeScheme(lines []string) string {
	// This is a simplified implementation
	// In a real-world scenario, you would use phonetic analysis

	if len(lines) < 2 {
		return "Unknown"
	}

	// Extract last word of each line
	lastWords := make([]string, len(lines))
	for i, line := range lines {
		words := strings.Fields(line)
		if len(words) > 0 {
			lastWords[i] = strings.ToLower(words[len(words)-1])
			// Remove punctuation
			lastWords[i] = strings.TrimFunc(lastWords[i], func(r rune) bool {
				return !unicode.IsLetter(r)
			})
		}
	}

	// Check for common rhyme schemes

	// Check for AABB pattern
	if len(lines) >= 4 {
		if endsMatch(lastWords[0], lastWords[1]) && endsMatch(lastWords[2], lastWords[3]) {
			return "AABB"
		}
	}

	// Check for ABAB pattern
	if len(lines) >= 4 {
		if endsMatch(lastWords[0], lastWords[2]) && endsMatch(lastWords[1], lastWords[3]) {
			return "ABAB"
		}
	}

	// Check for ABBA pattern
	if len(lines) >= 4 {
		if endsMatch(lastWords[0], lastWords[3]) && endsMatch(lastWords[1], lastWords[2]) {
			return "ABBA"
		}
	}

	// Check for AAAA pattern
	if len(lines) >= 4 {
		if endsMatch(lastWords[0], lastWords[1]) && endsMatch(lastWords[1], lastWords[2]) && endsMatch(lastWords[2], lastWords[3]) {
			return "AAAA"
		}
	}

	// Default
	return "Irregular"
}

// detectMeterType detects the meter type of a poem
func detectMeterType(lines []string) string {
	// This is a simplified implementation
	// In a real-world scenario, you would use syllable counting and stress patterns

	if len(lines) == 0 {
		return "Unknown"
	}

	// Count syllables in each line
	syllableCounts := make([]int, len(lines))
	for i, line := range lines {
		syllableCounts[i] = countSyllables(line)
	}

	// Check for common meter types

	// Check for iambic pentameter (around 10 syllables per line)
	pentameterCount := 0
	for _, count := range syllableCounts {
		if count >= 9 && count <= 11 {
			pentameterCount++
		}
	}
	if float64(pentameterCount)/float64(len(lines)) > 0.7 {
		return "Iambic Pentameter"
	}

	// Check for tetrameter (around 8 syllables per line)
	tetrameterCount := 0
	for _, count := range syllableCounts {
		if count >= 7 && count <= 9 {
			tetrameterCount++
		}
	}
	if float64(tetrameterCount)/float64(len(lines)) > 0.7 {
		return "Tetrameter"
	}

	// Check for trimeter (around 6 syllables per line)
	trimeterCount := 0
	for _, count := range syllableCounts {
		if count >= 5 && count <= 7 {
			trimeterCount++
		}
	}
	if float64(trimeterCount)/float64(len(lines)) > 0.7 {
		return "Trimeter"
	}

	// Default
	return "Free Verse"
}

// determineStructure determines the overall structure of a poem
func determineStructure(stanzaCount, lineCount int, rhymeScheme, meterType string) string {
	// This is a simplified implementation

	// Check for common poetic forms

	// Sonnet
	if lineCount == 14 && (rhymeScheme == "ABAB" || rhymeScheme == "ABBA") && meterType == "Iambic Pentameter" {
		return "Sonnet"
	}

	// Haiku
	if lineCount == 3 && stanzaCount == 1 {
		return "Haiku"
	}

	// Limerick
	if lineCount == 5 && stanzaCount == 1 && rhymeScheme == "AABBA" {
		return "Limerick"
	}

	// Quatrain
	if lineCount%4 == 0 && stanzaCount == lineCount/4 {
		return "Quatrain"
	}

	// Tercet
	if lineCount%3 == 0 && stanzaCount == lineCount/3 {
		return "Tercet"
	}

	// Couplet
	if lineCount%2 == 0 && stanzaCount == lineCount/2 {
		return "Couplet"
	}

	// Default
	return "Free Form"
}

// endsMatch checks if two words have matching endings (simplified rhyme detection)
func endsMatch(word1, word2 string) bool {
	// This is a simplified implementation
	// In a real-world scenario, you would use phonetic analysis

	if len(word1) < 2 || len(word2) < 2 {
		return false
	}

	// Check if the last 2 characters match
	return word1[len(word1)-2:] == word2[len(word2)-2:]
}

// countSyllables counts the syllables in a line of text
func countSyllables(line string) int {
	// This is a simplified implementation
	// In a real-world scenario, you would use a dictionary or ML model

	words := strings.Fields(line)
	syllableCount := 0

	for _, word := range words {
		// Clean the word
		word = strings.ToLower(word)
		word = strings.TrimFunc(word, func(r rune) bool {
			return !unicode.IsLetter(r)
		})

		// Count vowel groups
		inVowelGroup := false
		for i, r := range word {
			isVowelChar := isVowel(r)

			// Start of vowel group
			if isVowelChar && !inVowelGroup {
				inVowelGroup = true
				syllableCount++
			}

			// End of vowel group
			if !isVowelChar {
				inVowelGroup = false
			}

			// Handle silent e at the end
			if i == len(word)-1 && r == 'e' && i > 0 {
				prevChar := rune(word[i-1])
				if !isVowel(prevChar) {
					syllableCount--
				}
			}
		}

		// Ensure at least one syllable per word
		if syllableCount == 0 {
			syllableCount = 1
		}
	}

	return syllableCount
}

// isVowel checks if a character is a vowel
func isVowel(r rune) bool {
	return r == 'a' || r == 'e' || r == 'i' || r == 'o' || r == 'u' || r == 'y'
}
