package handler

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

// 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 {
			ErrorHandler(w, r, 404)
			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 {
			ErrorHandler(w, r, 404)
			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 {
			ErrorHandler(w, r, 404)
			return
		}
		// Send the request and get the response
		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil || resp.StatusCode != http.StatusOK {
			ErrorHandler(w, r, 404)
			return
		}
		defer resp.Body.Close()
		// Read the response body into a byte slice
		fileContent, err := io.ReadAll(resp.Body)
		if err != nil {
			ErrorHandler(w, r, 404)
			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)
	if err != nil {
		// If file not found, try replacing ' ' with '+' and check again
		tempFilePath = strings.ReplaceAll(fullFilePath, " ", "+")
		fileContent, err = GetFileContent(tempFilePath)
		if err != nil {
			ErrorHandler(w, r, 404)
			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)
}
