package deno

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"
)

// RouteEntry represents a parsed API endpoint from a TypeScript file.
type RouteEntry struct {
	Method       string // GET, POST, PUT, DELETE, PATCH
	Path         string // e.g., "/api/weather/current"
	ImportPath   string // relative import path, e.g., "./api/weather.ts"
	FunctionName string // original export name, e.g., "API_get_current"
}

// validMethods lists the HTTP methods recognized by the convention.
var validMethods = map[string]bool{
	"get": true, "post": true, "put": true, "delete": true, "patch": true,
}

// apiExportPattern matches "export async function API_..." or "export function API_..."
var apiExportPattern = regexp.MustCompile(`^export\s+(?:async\s+)?function\s+(API_\w+)`)

// ScanRoutes scans all .ts files in the api/ subdirectory for API_ prefixed exports
// and returns a list of RouteEntry for each discovered endpoint.
func ScanRoutes(apiDir string) ([]RouteEntry, error) {
	var routes []RouteEntry

	err := filepath.Walk(apiDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		ext := strings.ToLower(filepath.Ext(path))
		if ext != ".ts" && ext != ".js" {
			return nil
		}

		// Derive the base route from file path relative to apiDir.
		relPath, err := filepath.Rel(apiDir, path)
		if err != nil {
			return fmt.Errorf("rel path: %w", err)
		}
		// Convert "weather.ts" → "weather", "sub/weather.ts" → "sub/weather"
		baseName := strings.TrimSuffix(relPath, filepath.Ext(relPath))
		baseName = filepath.ToSlash(baseName)
		baseRoute := "/api/" + baseName

		// Import path relative to the generated router.
		importPath := "./api/" + filepath.ToSlash(relPath)

		// Scan file for API_ exports.
		fileRoutes, err := scanFileExports(path, baseRoute, importPath)
		if err != nil {
			return fmt.Errorf("scan %s: %w", relPath, err)
		}
		routes = append(routes, fileRoutes...)

		return nil
	})

	return routes, err
}

// scanFileExports reads a TypeScript file and extracts API_ prefixed exports.
func scanFileExports(filePath, baseRoute, importPath string) ([]RouteEntry, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf("open file: %w", err)
	}
	defer file.Close()

	var routes []RouteEntry
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		matches := apiExportPattern.FindStringSubmatch(line)
		if len(matches) < 2 {
			continue
		}

		funcName := matches[1] // e.g., "API_get_current"
		entry, ok := parseAPIFunctionName(funcName, baseRoute, importPath)
		if ok {
			routes = append(routes, entry)
		}
	}

	return routes, scanner.Err()
}

// parseAPIFunctionName parses "API_get_current" into a RouteEntry.
// Convention: API_{method} → handles base route; API_{method}_{path} → handles sub-route.
func parseAPIFunctionName(funcName, baseRoute, importPath string) (RouteEntry, bool) {
	// Strip "API_" prefix.
	rest := strings.TrimPrefix(funcName, "API_")
	if rest == "" {
		return RouteEntry{}, false
	}

	// Split on first "_" to extract method.
	parts := strings.SplitN(rest, "_", 2)
	method := strings.ToLower(parts[0])
	if !validMethods[method] {
		return RouteEntry{}, false
	}

	path := baseRoute
	if len(parts) == 2 && parts[1] != "" {
		path = baseRoute + "/" + parts[1]
	}

	return RouteEntry{
		Method:       strings.ToUpper(method),
		Path:         path,
		ImportPath:   importPath,
		FunctionName: funcName,
	}, true
}

// routerTemplate is the Deno router entrypoint template.
var routerTemplate = template.Must(template.New("router").Parse(`// Auto-generated by Tinyforge — do not edit manually.
import { serveDir } from "https://deno.land/std/http/file_server.ts";

{{- range .Imports}}
import { {{.FunctionName}} as {{.Alias}} } from "{{.Path}}";
{{- end}}

const routes: Array<{ method: string; path: string; handler: (req: Request) => Promise<Response> | Response }> = [
{{- range .Routes}}
  { method: "{{.Method}}", path: "{{.Path}}", handler: {{.Alias}} },
{{- end}}
];

Deno.serve({ port: 8000 }, async (req: Request): Promise<Response> => {
  const url = new URL(req.url);
  const method = req.method.toUpperCase();

  // Match API routes.
  for (const route of routes) {
    if (route.method === method && url.pathname === route.path) {
      try {
        return await route.handler(req);
      } catch (e) {
        console.error("Handler error:", e);
        return new Response(JSON.stringify({ error: "Internal server error" }), {
          status: 500,
          headers: { "Content-Type": "application/json" },
        });
      }
    }
  }

  // Serve static files from /public.
  return serveDir(req, { fsRoot: "/app/public", quiet: true });
});
`))

// ImportEntry represents a single aliased import.
type ImportEntry struct {
	FunctionName string // original name, e.g., "API_get"
	Alias        string // unique alias, e.g., "time_API_get"
	Path         string // import path, e.g., "./api/time.ts"
}

// routerData holds the data for the router template.
type routerData struct {
	Imports []ImportEntry
	Routes  []routeWithAlias
}

// routeWithAlias is a route entry using the aliased handler name.
type routeWithAlias struct {
	Method string
	Path   string
	Alias  string
}

// GenerateRouter generates the Deno router TypeScript source from route entries.
func GenerateRouter(routes []RouteEntry) (string, error) {
	var imports []ImportEntry
	var aliasedRoutes []routeWithAlias

	for _, r := range routes {
		// Derive a unique alias from the file base name + function name.
		// e.g., "./api/echo.ts" + "API_get" → "echo_API_get"
		baseName := filepath.Base(r.ImportPath)
		baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
		alias := baseName + "_" + r.FunctionName

		imports = append(imports, ImportEntry{
			FunctionName: r.FunctionName,
			Alias:        alias,
			Path:         r.ImportPath,
		})

		aliasedRoutes = append(aliasedRoutes, routeWithAlias{
			Method: r.Method,
			Path:   r.Path,
			Alias:  alias,
		})
	}

	data := routerData{Imports: imports, Routes: aliasedRoutes}

	var buf strings.Builder
	if err := routerTemplate.Execute(&buf, data); err != nil {
		return "", fmt.Errorf("execute router template: %w", err)
	}

	return buf.String(), nil
}

// GenerateDockerfile generates the Dockerfile for the Deno container.
func GenerateDockerfile() string {
	return `FROM denoland/deno:latest

WORKDIR /app

# Copy static files.
COPY public/ /app/public/

# Copy API source files and generated router.
COPY api/ /app/api/
COPY router.ts /app/router.ts

# Create data directory for persistent storage (mounted as volume when enabled).
RUN mkdir -p /app/data

# Cache dependencies.
RUN deno install --entrypoint router.ts

EXPOSE 8000

CMD ["deno", "run", "--allow-net", "--allow-read", "--allow-write=/app/data", "--allow-env", "router.ts"]
`
}


// GenerateStaticDockerfile generates a minimal nginx Dockerfile for pure static sites.
func GenerateStaticDockerfile() string {
	return `FROM nginx:alpine

COPY . /usr/share/nginx/html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
`
}
