package services

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

// TestCalculateFileSHA256 tests the SHA-256 calculation with streaming
func TestCalculateFileSHA256(t *testing.T) {
	scanner := &MediaScanner{}

	// Create a temporary test file
	tmpDir := t.TempDir()
	testFile := filepath.Join(tmpDir, "test.txt")
	testContent := "The quick brown fox jumps over the lazy dog"

	if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
		t.Fatalf("Failed to create test file: %v", err)
	}

	// Calculate expected hash
	hasher := sha256.New()
	hasher.Write([]byte(testContent))
	expectedHash := hex.EncodeToString(hasher.Sum(nil))

	// Test the function
	calculatedHash, err := scanner.calculateFileSHA256(testFile)
	if err != nil {
		t.Fatalf("calculateFileSHA256 failed: %v", err)
	}

	if calculatedHash != expectedHash {
		t.Errorf("Expected hash %s, got %s", expectedHash, calculatedHash)
	}

	t.Logf("SHA-256 hash calculation successful: %s", calculatedHash)
}

// TestCalculateFileSHA256LargeFile tests streaming with large file
func TestCalculateFileSHA256LargeFile(t *testing.T) {
	scanner := &MediaScanner{}

	// Create a temporary test file with larger content
	tmpDir := t.TempDir()
	testFile := filepath.Join(tmpDir, "large_test.txt")

	// Create a 10MB file
	file, err := os.Create(testFile)
	if err != nil {
		t.Fatalf("Failed to create test file: %v", err)
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	testLine := strings.Repeat("This is a test line for SHA-256 calculation\n", 100)
	for i := 0; i < 1000; i++ {
		if _, err := writer.WriteString(testLine); err != nil {
			t.Fatalf("Failed to write to test file: %v", err)
		}
	}
	writer.Flush()

	// Calculate expected hash
	file.Seek(0, 0)
	hasher := sha256.New()
	buf := make([]byte, 4096)
	for {
		n, err := file.Read(buf)
		if err != nil && !errors.Is(err, bufio.ErrBufferFull) {
			if err == io.EOF {
				break
			}
			t.Fatalf("Failed to read file for expected hash: %v", err)
		}
		hasher.Write(buf[:n])
		if err == io.EOF {
			break
		}
	}
	expectedHash := hex.EncodeToString(hasher.Sum(nil))

	// Test the function
	calculatedHash, err := scanner.calculateFileSHA256(testFile)
	if err != nil {
		t.Fatalf("calculateFileSHA256 failed for large file: %v", err)
	}

	if calculatedHash != expectedHash {
		t.Errorf("Expected hash %s, got %s", expectedHash, calculatedHash)
	}

	t.Logf("Large file SHA-256 hash calculation successful")
}

// TestExtractISBNFromIdentifier tests ISBN extraction
func TestExtractISBNFromIdentifier(t *testing.T) {
	scanner := &MediaScanner{}

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "ISBN with prefix",
			input:    "isbn:978-3-16-148410-0",
			expected: "9783161484100",
		},
		{
			name:     "ISBN with hyphens",
			input:    "978-3-16-148410-0",
			expected: "9783161484100",
		},
		{
			name:     "ISBN with spaces",
			input:    "978 3 16 148410 0",
			expected: "9783161484100",
		},
		{
			name:     "ISBN-10",
			input:    "isbn:0-306-40615-2",
			expected: "0306406152",
		},
		{
			name:     "Clean ISBN-13",
			input:    "9783161484100",
			expected: "9783161484100",
		},
		{
			name:     "Invalid identifier",
			input:    "not-an-isbn",
			expected: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := scanner.extractISBNFromIdentifier(tt.input)
			if result != tt.expected {
				t.Errorf("Expected %s, got %s", tt.expected, result)
			}
		})
	}
}

// TestIsValidUUID tests UUID validation
func TestIsValidUUID(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected bool
	}{
		{
			name:     "Valid UUID v4",
			input:    "550e8400-e29b-41d4-a716-446655440000",
			expected: true,
		},
		{
			name:     "Valid UUID with uppercase",
			input:    "550E8400-E29B-41D4-A716-446655440000",
			expected: true,
		},
		{
			name:     "Invalid UUID - missing dashes",
			input:    "550e8400e29b41d4a716446655440000",
			expected: false,
		},
		{
			name:     "Invalid UUID - wrong format",
			input:    "not-a-uuid",
			expected: false,
		},
		{
			name:     "Empty string",
			input:    "",
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := isValidUUID(tt.input)
			if result != tt.expected {
				t.Errorf("Expected %v, got %v for input %s", tt.expected, result, tt.input)
			}
		})
	}
}

// TestDetermineHashConfidence tests confidence level determination
func TestDetermineHashConfidence(t *testing.T) {
	scanner := &MediaScanner{}

	tests := []struct {
		name         string
		uuid         string
		identifier   string
		expectedConf string
	}{
		{
			name:         "High confidence - valid UUID",
			uuid:         "550e8400-e29b-41d4-a716-446655440000",
			identifier:   "",
			expectedConf: "high",
		},
		{
			name:         "Medium confidence - ISBN",
			uuid:         "",
			identifier:   "isbn:978-3-16-148410-0",
			expectedConf: "medium",
		},
		{
			name:         "Medium confidence - long identifier",
			uuid:         "",
			identifier:   "some-long-identifier-string",
			expectedConf: "medium",
		},
		{
			name:         "Low confidence - no identifiers",
			uuid:         "",
			identifier:   "",
			expectedConf: "low",
		},
		{
			name:         "Low confidence - short identifier",
			uuid:         "",
			identifier:   "abc",
			expectedConf: "low",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := scanner.determineHashConfidence(tt.uuid, tt.identifier)
			if result != tt.expectedConf {
				t.Errorf("Expected confidence %s, got %s", tt.expectedConf, result)
			}
		})
	}
}

// TestDetectFormatType tests format type detection
func TestDetectFormatType(t *testing.T) {
	scanner := &MediaScanner{}

	tests := []struct {
		name         string
		filePath     string
		expectedType string
	}{
		{
			name:         "EPUB file",
			filePath:     "/path/to/book.epub",
			expectedType: "epub",
		},
		{
			name:         "KEPUB file",
			filePath:     "/path/to/book.kepub.epub",
			expectedType: "kepub",
		},
		{
			name:         "PDF file",
			filePath:     "/path/to/document.pdf",
			expectedType: "pdf",
		},
		{
			name:         "CBZ file",
			filePath:     "/path/to/comic.cbz",
			expectedType: "comic_archive",
		},
		{
			name:         "MOBI file",
			filePath:     "/path/to/book.mobi",
			expectedType: "mobi",
		},
		{
			name:         "TXT file",
			filePath:     "/path/to/book.txt",
			expectedType: "txt",
		},
		{
			name:         "Unknown format",
			filePath:     "/path/to/book.xyz",
			expectedType: "unknown",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := scanner.detectFormatType(tt.filePath)
			if result != tt.expectedType {
				t.Errorf("Expected format type %s, got %s", tt.expectedType, result)
			}
		})
	}
}

// TestExtractHashInfo tests the complete hash extraction flow
func TestExtractHashInfo(t *testing.T) {
	scanner := &MediaScanner{}

	// Create a temporary test file
	tmpDir := t.TempDir()
	testFile := filepath.Join(tmpDir, "test_book.txt")
	testContent := "Test book content for hash extraction"

	if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
		t.Fatalf("Failed to create test file: %v", err)
	}

	// Test hash extraction
	hashInfo, formatInfo, err := scanner.extractHashInfo(testFile)
	if err != nil {
		t.Fatalf("extractHashInfo failed: %v", err)
	}

	// Verify hash info
	if hashInfo == nil {
		t.Fatal("hashInfo is nil")
	}

	if hashInfo.FileSHA256 == "" {
		t.Error("FileSHA256 is empty")
	}

	// Verify format info
	if formatInfo == nil {
		t.Fatal("formatInfo is nil")
	}

	if formatInfo.FormatType != "txt" {
		t.Errorf("Expected format type 'txt', got %s", formatInfo.FormatType)
	}

	if formatInfo.FileSHA256 != hashInfo.FileSHA256 {
		t.Error("FormatInfo.FileSHA256 doesn't match HashInfo.FileSHA256")
	}

	t.Logf("Hash extraction test passed: SHA256=%s, Format=%s, Confidence=%s",
		hashInfo.FileSHA256, formatInfo.FormatType, hashInfo.HashConfidence)
}

// BenchmarkCalculateFileSHA256 benchmarks SHA-256 calculation
func BenchmarkCalculateFileSHA256(b *testing.B) {
	scanner := &MediaScanner{}

	// Create a temporary test file
	tmpDir := b.TempDir()
	testFile := filepath.Join(tmpDir, "bench_test.txt")
	testContent := strings.Repeat("Benchmark test content for SHA-256 calculation\n", 10000)

	if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
		b.Fatalf("Failed to create test file: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = scanner.calculateFileSHA256(testFile)
	}
}

// TestExtractHashInfoIntegration is an integration test that tests a realistic scenario
func TestExtractHashInfoIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	scanner := &MediaScanner{}
	tmpDir := t.TempDir()

	// Test multiple file types
	testFiles := []struct {
		name     string
		filename string
		content  string
	}{
		{
			name:     "Text file",
			filename: "test.txt",
			content:  "Plain text file",
		},
		{
			name:     "HTML file (simulating EPUB content)",
			filename: "test.html",
			content:  "<html><body>Test content</body></html>",
		},
	}

	for _, tf := range testFiles {
		t.Run(tf.name, func(t *testing.T) {
			testFile := filepath.Join(tmpDir, tf.filename)
			if err := os.WriteFile(testFile, []byte(tf.content), 0644); err != nil {
				t.Fatalf("Failed to create test file: %v", err)
			}

			hashInfo, formatInfo, err := scanner.extractHashInfo(testFile)
			if err != nil {
				t.Fatalf("extractHashInfo failed: %v", err)
			}

			// Verify SHA-256 is calculated
			if hashInfo.FileSHA256 == "" {
				t.Error("SHA-256 hash not calculated")
			}

			// Verify format detection
			if formatInfo.FormatType == "unknown" {
				t.Logf("Warning: Format detected as 'unknown' for %s", tf.filename)
			}

			t.Logf("Integration test passed for %s: SHA256=%s, Format=%s",
				tf.name, hashInfo.FileSHA256, formatInfo.FormatType)
		})
	}
}

// Example usage
func ExampleMediaScanner_calculateFileSHA256() {
	scanner := &MediaScanner{}

	// Calculate SHA-256 hash of a file
	hash, err := scanner.calculateFileSHA256("/path/to/ebook.epub")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("SHA-256: %s\n", hash)
}
