package backend

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

type SwaggerFile struct {
	Name         string `json:"name"`
	Path         string `json:"path"`
	Size         int64  `json:"size"`
	ModifiedTime string `json:"modifiedTime"`
}

type SwaggerServer struct {
	server  *http.Server
	port    int
	running bool
	mu      sync.Mutex
}

var swaggerServer *SwaggerServer
var swaggerServerMu sync.Mutex

func (a *App) GetSwaggerFiles(dirPath string) ([]SwaggerFile, error) {
	if dirPath == "" {
		return nil, fmt.Errorf("directory path is empty")
	}

	var files []SwaggerFile

	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		if strings.HasSuffix(strings.ToLower(info.Name()), ".swagger.json") {
			files = append(files, SwaggerFile{
				Name:         info.Name(),
				Path:         path,
				Size:         info.Size(),
				ModifiedTime: info.ModTime().Format("2006-01-02 15:04:05"),
			})
		}

		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("failed to walk directory: %w", err)
	}

	return files, nil
}

func (a *App) GetSwaggerFileInfo(filePath string) (SwaggerFile, error) {
	if filePath == "" {
		return SwaggerFile{}, fmt.Errorf("file path is empty")
	}

	info, err := os.Stat(filePath)
	if err != nil {
		return SwaggerFile{}, fmt.Errorf("failed to get file info: %w", err)
	}

	return SwaggerFile{
		Name:         info.Name(),
		Path:         filePath,
		Size:         info.Size(),
		ModifiedTime: info.ModTime().Format("2006-01-02 15:04:05"),
	}, nil
}

func (a *App) ReadSwaggerFile(filePath string) (string, error) {
	if filePath == "" {
		return "", fmt.Errorf("file path is empty")
	}

	content, err := os.ReadFile(filePath)
	if err != nil {
		return "", fmt.Errorf("failed to read file: %w", err)
	}

	return string(content), nil
}

func (a *App) StartSwaggerServer(filePath string) (string, error) {
	if filePath == "" {
		return "", fmt.Errorf("file path is empty")
	}

	swaggerServerMu.Lock()
	defer swaggerServerMu.Unlock()

	if swaggerServer != nil && swaggerServer.running {
		return fmt.Sprintf("http://localhost:%d", swaggerServer.port), nil
	}

	port := 8080
	for i := 0; i < 100; i++ {
		listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
		if err == nil {
			listener.Close()
			break
		}
		port++
	}

	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		swaggerHTML := `<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Swagger UI</title>
    <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui.css">
    <style>
        html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
        *, *:before, *:after { box-sizing: inherit; }
        body { margin: 0; background: #fafafa; }
        .topbar { display: none; }
        .swagger-ui .topbar { display: none; }
    </style>
</head>
<body>
    <div id="swagger-ui"></div>
    <script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-bundle.js"></script>
    <script>
        window.onload = function() {
            const ui = SwaggerUIBundle({
                url: '/swagger.json',
                dom_id: '#swagger-ui',
                presets: [
                    SwaggerUIBundle.presets.apis,
                    SwaggerUIBundle.SwaggerUIStandalonePreset
                ],
                layout: "BaseLayout",
                deepLinking: true,
                showExtensions: true,
                showCommonExtensions: true,
                docExpansion: "list",
                filter: true,
                tryItOutEnabled: true
            });
        };
    </script>
</body>
</html>`
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write([]byte(swaggerHTML))
	})

	mux.HandleFunc("/swagger.json", func(w http.ResponseWriter, r *http.Request) {
		content, err := os.ReadFile(filePath)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(content)
	})

	server := &http.Server{
		Addr:         fmt.Sprintf(":%d", port),
		Handler:      mux,
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 15 * time.Second,
	}

	swaggerServer = &SwaggerServer{
		server:  server,
		port:    port,
		running: true,
	}

	go func() {
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			fmt.Printf("Swagger server error: %v\n", err)
		}
	}()

	return fmt.Sprintf("http://localhost:%d", port), nil
}

func (a *App) StopSwaggerServer() error {
	swaggerServerMu.Lock()
	defer swaggerServerMu.Unlock()

	if swaggerServer == nil || !swaggerServer.running {
		return nil
	}

	if err := swaggerServer.server.Close(); err != nil {
		return fmt.Errorf("failed to stop server: %w", err)
	}

	swaggerServer.running = false
	return nil
}

func (a *App) IsSwaggerServerRunning() bool {
	swaggerServerMu.Lock()
	defer swaggerServerMu.Unlock()

	if swaggerServer == nil {
		return false
	}
	return swaggerServer.running
}

func (a *App) GetSwaggerServerURL() string {
	swaggerServerMu.Lock()
	defer swaggerServerMu.Unlock()

	if swaggerServer == nil || !swaggerServer.running {
		return ""
	}
	return fmt.Sprintf("http://localhost:%d", swaggerServer.port)
}
