//
//  Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

package source

import (
	"fmt"
	"net/url"
	"path/filepath"
	"ragflow/internal/common"
	"strings"
)

// SkillSource is the interface for skill sources
type SkillSource interface {
	// SourceID returns the source identifier (local, github, clawhub, skillssh)
	SourceID() string

	// Fetch downloads and returns the skill bundle
	Fetch(identifier string) (*SkillBundle, error)

	// Inspect retrieves metadata without downloading full content
	Inspect(identifier string) (*SkillMetadata, error)

	// TrustLevel returns the trust level for this source (builtin/trusted/community)
	TrustLevel(identifier string) string
}

// SourceResolver resolves source references to appropriate adapters
type SourceResolver struct {
	sources map[string]SkillSource
}

// NewSourceResolver creates a new source resolver
func NewSourceResolver(client HTTPClientInterface) *SourceResolver {
	return &SourceResolver{
		sources: map[string]SkillSource{
			"local":    NewLocalSource(),
			"github":   NewGitHubSource(client),
			"clawhub":  NewClawHubSource(client),
			"skillssh": NewSkillsShSource(client),
		},
	}
}

// Resolve parses a source reference and returns the appropriate source adapter
// Supported formats:
//   - ./path, /absolute/path -> local
//   - github.com/owner/repo/path -> github
//   - clawhub://owner/skill-name, clawhub.ai/owner/skill-name -> clawhub
//   - skill://skill-name, skills.sh/skill/name -> skillssh
func (r *SourceResolver) Resolve(ref string) (SkillSource, string, error) {
	ref = strings.TrimSpace(ref)
	if ref == "" {
		return nil, "", fmt.Errorf("empty source reference")
	}

	// Check for URI schemes
	if strings.HasPrefix(ref, "clawhub://") {
		identifier := strings.TrimPrefix(ref, "clawhub://")
		return r.sources["clawhub"], identifier, nil
	}
	if strings.HasPrefix(ref, "skill://") {
		identifier := strings.TrimPrefix(ref, "skill://")
		return r.sources["skillssh"], identifier, nil
	}

	// Check for local path (starts with ./ or / or ~)
	if strings.HasPrefix(ref, "./") || strings.HasPrefix(ref, "/") || strings.HasPrefix(ref, "~/") {
		// Expand ~ to home directory
		if strings.HasPrefix(ref, "~/") {
			home, err := getHomeDir()
			if err != nil {
				return nil, "", fmt.Errorf("cannot resolve home directory: %w", err)
			}
			ref = filepath.Join(home, ref[2:])
		}
		return r.sources["local"], ref, nil
	}

	// Check for github.com domain
	if strings.HasPrefix(ref, "github.com/") || strings.HasPrefix(ref, "https://github.com/") {
		identifier := strings.TrimPrefix(ref, "https://")
		return r.sources["github"], identifier, nil
	}

	// Check for clawhub.ai domain
	if strings.HasPrefix(ref, "clawhub.ai/") || strings.HasPrefix(ref, "https://clawhub.ai/") {
		identifier := strings.TrimPrefix(ref, "https://")
		identifier = strings.TrimPrefix(identifier, "clawhub.ai/")
		return r.sources["clawhub"], identifier, nil
	}

	// Check for skills.sh domain
	if strings.HasPrefix(ref, "skills.sh/") || strings.HasPrefix(ref, "https://skills.sh/") {
		identifier := strings.TrimPrefix(ref, "https://")
		identifier = strings.TrimPrefix(identifier, "skills.sh/")
		return r.sources["skillssh"], identifier, nil
	}

	// Default: treat as local path if it exists, otherwise error
	return r.sources["local"], ref, nil
}

// getHomeDir returns the user's home directory
func getHomeDir() (string, error) {
	home := common.GetEnv(common.EnvHome)
	if home == "" {
		home = common.GetEnv(common.EnvUserProfile)
	}
	if home == "" {
		return "", fmt.Errorf("cannot determine home directory")
	}
	return home, nil
}

// parseGitHubURL parses a GitHub URL and returns owner, repo, and path
func parseGitHubURL(urlStr string) (owner, repo, path string, err error) {
	// Remove protocol prefix if present
	urlStr = strings.TrimPrefix(urlStr, "https://")
	urlStr = strings.TrimPrefix(urlStr, "http://")

	// Remove github.com/ prefix
	urlStr = strings.TrimPrefix(urlStr, "github.com/")

	parts := strings.Split(urlStr, "/")
	if len(parts) < 2 {
		return "", "", "", fmt.Errorf("invalid GitHub URL format")
	}

	owner = parts[0]
	repo = parts[1]
	if len(parts) > 2 {
		path = strings.Join(parts[2:], "/")
	}

	return owner, repo, path, nil
}

// extractSkillNameFromPath extracts the skill name from a path
func extractSkillNameFromPath(path string) string {
	base := filepath.Base(path)
	// Remove common suffixes
	base = strings.TrimSuffix(base, ".git")
	return base
}

// isTrustedGitHubRepo checks if a GitHub repo is trusted
func isTrustedGitHubRepo(owner, repo string) bool {
	fullName := owner + "/" + repo
	trusted := map[string]bool{
		"openai/skills":     true,
		"anthropics/skills": true,
		"microsoft/skills":  true,
		"google/skills":     true,
	}
	return trusted[fullName]
}

// Helper to check if URL is valid
func isValidURL(str string) bool {
	u, err := url.Parse(str)
	return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
}
