// Copyright 2025 CloudWeGo Authors
//
// 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
//
//     https://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 parser

import (
	"encoding/json"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"testing"

	"github.com/cloudwego/abcoder/lang/testutils"
	. "github.com/cloudwego/abcoder/lang/uniast"
)

const localSessURL = "github.com/cloudwego/localsession"

func Test_goParser_ParseRepo(t *testing.T) {
	type fields struct {
		modName     string
		homePageDir string
		id          Identity
	}
	tests := []struct {
		name   string
		fields fields
	}{
		{
			name: "test",
			fields: fields{
				modName:     localSessURL,
				homePageDir: "localsession",
				id:          NewIdentity(localSessURL, localSessURL+"/backup", "RecoverCtxOnDemands"),
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			repoDir, err := testutils.GitCloneFast(tt.fields.modName, tt.fields.homePageDir, "main")
			if err != nil {
				t.Fatalf("failed to clone repo %s", err)
			}
			p := newGoParser(tt.fields.modName, repoDir, Options{
				ReferCodeDepth: 1,
				NeedTest:       true,
			})
			r, err := p.ParseRepo()
			if err != nil {
				t.Fatalf("failed to parse repo %s", err)
			}
			r.BuildGraph()
			_, err = p.getNode(tt.fields.id)
			if err != nil {
				t.Fatalf("failed to get node %s", err)
			}
		})
	}
}

func Test_goParser_ParseDirs(t *testing.T) {
	type args struct {
		modName     string
		homePageDir string
		pkg         string
		opts        Options
	}
	tests := []struct {
		name    string
		args    args
		wantRet map[string]*Function
		wantErr bool
	}{
		{
			name: "test",
			args: args{
				homePageDir: testutils.FirstTest("go"),
				modName:     "a.b/c",
				pkg:         "a.b/c/cmd",
				opts: Options{
					ReferCodeDepth: 1,
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := newGoParser(tt.args.modName, tt.args.homePageDir, tt.args.opts)
			_, err := p.ParseRepo()
			if (err != nil) != tt.wantErr {
				t.Errorf("goParser.ParseDirs() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			out, err := json.MarshalIndent(p.repo, "", "  ")
			if err != nil {
				t.Fatal(err)
			}
			if err := os.WriteFile("golang.json", out, 0644); err != nil {
				t.Fatal(err)
			}
		})
	}
}

func TestGoAst(t *testing.T) {
	src := `
package parse

type Struct struct {
	A struct{
		B int
	}
}
	`
	fset := token.NewFileSet()
	node, err := parser.ParseFile(fset, "", src, 0)
	if err != nil {
		t.Fatal(err)
	}
	ast.Inspect(node, func(n ast.Node) bool {
		if sel, ok := n.(*ast.SelectorExpr); ok {
			println("selector:", string(GetRawContent(fset, []byte(src), sel, false)))
		}
		if stru, ok := n.(*ast.StructType); ok {
			println("struct:", string(GetRawContent(fset, []byte(src), stru, true)))
		}
		return true
	})
}

func Test_goParser_ParseNode(t *testing.T) {
	type fields struct {
		modName     string
		homePageDir string
	}
	type args struct {
		pkgPath string
		name    string
	}
	localSessionDir, err := testutils.GitCloneFast(localSessURL, "localsession", "main")
	if err != nil {
		t.Fatalf("failed to clone repo %s", err)
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
	}{
		{
			name: "test",
			fields: fields{
				modName:     "github.com/cloudwego/localsession",
				homePageDir: localSessionDir,
			},
			args: args{
				pkgPath: "github.com/modern-go/gls",
				name:    "DeleteGls",
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := NewParser(tt.fields.modName, tt.fields.homePageDir, Options{})
			got, err := p.ParseNode(tt.args.pkgPath, tt.args.name)
			if (err != nil) != tt.wantErr {
				t.Errorf("goParser.ParseNode() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			out, err := json.MarshalIndent(got, "", "  ")
			if err != nil {
				t.Fatal(err)
			}
			println(string(out))
		})
	}
}

func Test_matchMod(t *testing.T) {
	type args struct {
		impt    string
		modules map[string]string
	}
	tests := []struct {
		name     string
		args     args
		wantName string
		wantPath string
	}{
		{
			name: "test",
			args: args{
				impt: "github.com/xx/yy/zz",
				modules: map[string]string{
					"github.com/xx/yy":    "github.com/xx/yy@v1",
					"github.com/xx/yy/zz": "github.com/xx/yy/zz@v2",
					"github.com/xx/yy/bb": "github.com/xx/yy/bb@v3",
				},
			},
			wantName: "github.com/xx/yy/zz",
			wantPath: "github.com/xx/yy/zz@v2",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotName, gotPath := matchMod(tt.args.impt, tt.args.modules)
			if gotName != tt.wantName {
				t.Errorf("matchMod() gotName = %v, want %v", gotName, tt.wantName)
			}
			if gotPath != tt.wantPath {
				t.Errorf("matchMod() gotPath = %v, want %v", gotPath, tt.wantPath)
			}
		})
	}
}
