package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// ListFiles function: Traverse all files in the specified directory and return a slice of the file path
func ListFiles(dir string) ([]string, error) {
	var files []string
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			files = append(files, path)
		}
		return nil
	})
	return files, err
}

// Get Content function: Read the content of a specified file and return it
func GetFileContent(filePath string) ([]byte, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// Get File Size
	fileInfo, err := file.Stat()
	if err != nil {
		return nil, err
	}
	fileSize := fileInfo.Size()

	// Read File Content
	fileContent := make([]byte, fileSize)
	_, err = file.Read(fileContent)
	if err != nil {
		return nil, err
	}

	return fileContent, nil
}

// fileHandler function: Handle file requests
func fileHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Server", "MeowMusicEmbeddedServer")
	// Obtain the path of the request
	filePath := r.URL.Path

	// Check if the request path starts with "/url/"
	if strings.HasPrefix(filePath, "/url/") {
		// Extract the URL after "/url/"
		urlPath := filePath[len("/url/"):]
		// Decode the URL path in case it's URL encoded
		decodedURL, err := url.QueryUnescape(urlPath)
		if err != nil {
			NotFoundHandler(w, r)
			return
		}
		// Determine the protocol based on the URL path
		var protocol string
		if strings.HasPrefix(decodedURL, "http/") {
			protocol = "http://"
		} else if strings.HasPrefix(decodedURL, "https/") {
			protocol = "https://"
		} else {
			NotFoundHandler(w, r)
			return
		}
		// Remove the protocol part from the decoded URL
		decodedURL = strings.TrimPrefix(decodedURL, "http/")
		decodedURL = strings.TrimPrefix(decodedURL, "https/")
		// Correctly concatenate the protocol with the decoded URL
		decodedURL = protocol + decodedURL
		// Create a new HTTP request to the decoded URL, without copying headers
		req, err := http.NewRequest("GET", decodedURL, nil)
		if err != nil {
			NotFoundHandler(w, r)
			return
		}
		// Send the request and get the response
		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil || resp.StatusCode != http.StatusOK {
			NotFoundHandler(w, r)
			return
		}
		defer resp.Body.Close()
		// Read the response body into a byte slice
		fileContent, err := io.ReadAll(resp.Body)
		if err != nil {
			NotFoundHandler(w, r)
			return
		}
		// Set appropriate Content-Type based on file extension
		ext := filepath.Ext(decodedURL)
		switch ext {
		case ".mp3":
			w.Header().Set("Content-Type", "audio/mpeg")
		case ".wav":
			w.Header().Set("Content-Type", "audio/wav")
		case ".flac":
			w.Header().Set("Content-Type", "audio/flac")
		case ".aac":
			w.Header().Set("Content-Type", "audio/aac")
		case ".ogg":
			w.Header().Set("Content-Type", "audio/ogg")
		case ".m4a":
			w.Header().Set("Content-Type", "audio/mp4")
		case ".amr":
			w.Header().Set("Content-Type", "audio/amr")
		case ".jpg", ".jpeg":
			w.Header().Set("Content-Type", "image/jpeg")
		case ".png":
			w.Header().Set("Content-Type", "image/png")
		case ".gif":
			w.Header().Set("Content-Type", "image/gif")
		case ".bmp":
			w.Header().Set("Content-Type", "image/bmp")
		case ".svg":
			w.Header().Set("Content-Type", "image/svg+xml")
		case ".webp":
			w.Header().Set("Content-Type", "image/webp")
		case ".txt":
			w.Header().Set("Content-Type", "text/plain")
		case ".lrc":
			w.Header().Set("Content-Type", "text/plain")
		case ".mrc":
			w.Header().Set("Content-Type", "text/plain")
		case ".json":
			w.Header().Set("Content-Type", "application/json")
		default:
			w.Header().Set("Content-Type", "application/octet-stream")
		}
		// Write file content to response
		w.Write(fileContent)
		return
	}

	// Construct the complete file path
	fullFilePath := filepath.Join("./files", filePath)

	// Try replacing '+' with ' ' and check if the file exists
	tempFilePath := strings.ReplaceAll(fullFilePath, "+", " ")
	if _, err := os.Stat(tempFilePath); err == nil {
		fullFilePath = tempFilePath
	}

	// Get file content
	fileContent, err := GetFileContent(fullFilePath)
	
	// 检查是否为空文件（特别是 music.mp3，如果是空的也视为不存在，需要等待转码）
	if err == nil && len(fileContent) == 0 && strings.HasSuffix(filePath, "/music.mp3") {
		err = fmt.Errorf("file is empty")
	}
	
	if err != nil {
		// If file not found, try replacing ' ' with '+' and check again
		tempFilePath = strings.ReplaceAll(fullFilePath, " ", "+")
		fileContent, err = GetFileContent(tempFilePath)
		
		// 同样检查带 + 的路径是否为空
		if err == nil && len(fileContent) == 0 && strings.HasSuffix(filePath, "/music.mp3") {
			err = fmt.Errorf("file is empty")
		}

		if err != nil {
			// 特殊处理：如果请求的是缓存中的文件，等待后台处理完成
			fmt.Printf("[Web Access] File not found, checking path prefix: %s\n", filePath)
			if strings.HasPrefix(filePath, "/cache/music/") {
				// music.mp3 等待最多 60 秒，歌词等待最多 10 秒
				maxWait := 10
				if strings.HasSuffix(filePath, "/music.mp3") {
					maxWait = 60
				}
				
				// URL 解码路径（处理中文文件名）
				decodedPath, _ := url.QueryUnescape(filePath)
				decodedPath = strings.ReplaceAll(decodedPath, "+", " ") // + 转空格
				decodedFullPath := filepath.Join("./files", decodedPath)
				
				fmt.Printf("[Web Access] Waiting for file: %s (max %d seconds)\n", decodedFullPath, maxWait)
				for i := 0; i < maxWait; i++ {
					time.Sleep(1 * time.Second)
					// 检查 URL 解码后的路径
					if _, err := os.Stat(decodedFullPath); err == nil {
						fmt.Printf("[Web Access] File ready after %d seconds: %s\n", i+1, decodedFullPath)
						http.ServeFile(w, r, decodedFullPath)
						return
					}
					// 检查原始路径
					if _, err := os.Stat(fullFilePath); err == nil {
						http.ServeFile(w, r, fullFilePath)
						return
					}
					// 检查带 + 的路径
					tempPath := strings.ReplaceAll(fullFilePath, " ", "+")
					if _, err := os.Stat(tempPath); err == nil {
						http.ServeFile(w, r, tempPath)
						return
					}
				}
			}
			NotFoundHandler(w, r)
			return
		}
	}

	// Set appropriate Content-Type based on file extension
	ext := filepath.Ext(filePath)
	switch ext {
	case ".mp3":
		w.Header().Set("Content-Type", "audio/mpeg")
	case ".wav":
		w.Header().Set("Content-Type", "audio/wav")
	case ".flac":
		w.Header().Set("Content-Type", "audio/flac")
	case ".aac":
		w.Header().Set("Content-Type", "audio/aac")
	case ".ogg":
		w.Header().Set("Content-Type", "audio/ogg")
	case ".m4a":
		w.Header().Set("Content-Type", "audio/mp4")
	case ".amr":
		w.Header().Set("Content-Type", "audio/amr")
	case ".jpg", ".jpeg":
		w.Header().Set("Content-Type", "image/jpeg")
	case ".png":
		w.Header().Set("Content-Type", "image/png")
	case ".gif":
		w.Header().Set("Content-Type", "image/gif")
	case ".bmp":
		w.Header().Set("Content-Type", "image/bmp")
	case ".svg":
		w.Header().Set("Content-Type", "image/svg+xml")
	case ".webp":
		w.Header().Set("Content-Type", "image/webp")
	case ".txt":
		w.Header().Set("Content-Type", "text/plain")
	case ".lrc":
		w.Header().Set("Content-Type", "text/plain")
	case ".mrc":
		w.Header().Set("Content-Type", "text/plain")
	case ".json":
		w.Header().Set("Content-Type", "application/json")
	default:
		w.Header().Set("Content-Type", "application/octet-stream")
	}

	// Write file content to response
	w.Write(fileContent)
}
