package service

import (
	"context"
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"cloud.google.com/go/bigquery"
	"cloud.google.com/go/storage"
	"github.com/spf13/viper"
	"github.com/tidwall/gjson"
	bqv2 "google.golang.org/api/bigquery/v2"
	"google.golang.org/api/bigquerydatatransfer/v1"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"

	"data-platform/model"
	"data-platform/pkg/errors"
	"data-platform/pkg/log"
)

// GetConfigBool 从配置中获取布尔值，如果不存在则返回默认值
func GetConfigBool(key string, defaultValue bool) bool {
	if viper.IsSet(key) {
		return viper.GetBool(key)
	}
	return defaultValue
}

// FetchMonthlyOrderInfo 统一入口函数，根据配置选择使用新版本或旧版本
func FetchMonthlyOrderInfo(ctx context.Context, bqSrv *bigquery.Client, first bool) ([]*model.OrgBq, error) {
	// 检查配置来决定使用哪个版本
	// 默认使用新版本，如果设置了 datasource.use_legacy_data_source=true 则使用旧版本
	useLegacy := GetConfigBool("datasource.use_legacy_data_source", false)

	if useLegacy {
		log.L(ctx).Info("Using legacy data source")
		return FetchMonthlyOrderInfoLegacy(ctx, bqSrv, first)
	}

	log.L(ctx).Info("Using new data source")
	return FetchMonthlyOrderInfoV2(ctx, bqSrv, first)
}

// FetchMonthlyOrderInfoV2 新版本的数据获取函数，从两个新表获取数据
func FetchMonthlyOrderInfoV2(ctx context.Context, bqSrv *bigquery.Client, first bool) ([]*model.OrgBq, error) {
	log.L(ctx).Info("fetch monthly order info from new data sources")

	lastMonth := GetLastMonth()

	// Fetch data from revrec_order_test table
	revenueOrderData, err := fetchRevenueOrderData(ctx, bqSrv, lastMonth)
	if err != nil {
		log.L(ctx).Errorf("Failed to fetch revenue order data: %v", err)
		return nil, err
	}

	// Fetch data from revrec_org_test table
	collectionData, err := fetchCollectionData(ctx, bqSrv, lastMonth)
	if err != nil {
		log.L(ctx).Errorf("Failed to fetch collection data: %v", err)
		return nil, err
	}

	// Merge the data into OrgBq format
	mergedData, err := mergeRevenueAndCollectionData(ctx, revenueOrderData, collectionData)
	if err != nil {
		log.L(ctx).Errorf("Failed to merge revenue and collection data: %v", err)
		return nil, err
	}

	uniqueRecords := deDuplicateNewOrders(mergedData)

	log.L(ctx).Info("fetch monthly order info v2 success")

	return uniqueRecords, nil
}

func GetBigqueryData(ctx context.Context, bqSrv *bigquery.Client, querySql string) ([]*model.OrgBq, error) {
	log.L(ctx).Infof("get bq data from bigquery")

	query := bqSrv.Query(querySql)
	it, err := query.Read(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to read query: %v", err)

		return nil, err
	}
	res := make([]*model.OrgBq, 0)

	for {
		var value model.OrgBq
		err := it.Next(&value)
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.L(ctx).Errorf("Failed to read row: %v", err)

			return nil, err
		}

		res = append(res, &value)
	}

	log.L(ctx).Infof("get bq data success")

	return res, nil
}

func deDuplicateNewOrders(records []*model.OrgBq) []*model.OrgBq {
	duplicateMap := make(map[string]bool)
	var unique []*model.OrgBq

	for _, record := range records {
		if record.NeedNewOrder.Valid && record.NeedNewOrder.StringVal == "yes" {
			key := fmt.Sprintf("%s-%s", record.BillMonth.StringVal, record.AdjustTenantID.StringVal)
			if duplicateMap[key] {
				continue
			}
			duplicateMap[key] = true
		}
		unique = append(unique, record)
	}

	return unique
}

func WriteBqData(ctx context.Context, bqSrv *bigquery.Client, rows []*model.OrgBq) error {
	log.L(ctx).Infof("write bq data to bigquery")
	if len(rows) == 0 {
		return nil
	}

	lastMonth := GetLastMonth()

	// get csv from gcs
	client, err := storage.NewClient(ctx)
	// client, err := storage.NewClient(ctx, option.WithCredentialsFile(credsFilePath))
	if err != nil {
		return err
	}
	defer client.Close()

	// Create a handle to the bucket
	bucket := client.Bucket(dataPlatformBucket)
	// Create a handle to the object
	obj := bucket.Object(objectName)

	// Read the object
	r, err := obj.NewReader(ctx)
	if err != nil {
		return err
	}
	defer r.Close()

	// Read CSV contents
	csvReader := csv.NewReader(r)
	records, err := csvReader.ReadAll()
	if err != nil {
		return err
	}

	colIndexMap := make(map[string]int)
	for i, col := range records[0] {
		colIndexMap[col] = i
	}

	rowsMap := make(map[string]*model.OrgBq)
	for _, row := range rows {
		rowsMap[row.BillMonth.StringVal+row.AdjustTenantID.StringVal] = row
	}

	// del bill_month = lastMonth
	delCsv := make([]int, 0)
	for i, record := range records {
		if i == 0 {
			continue
		}
		if record[colIndexMap["bill_month"]] == lastMonth {
			delCsv = append(delCsv, i)
		}
	}

	// from right to left
	for i := len(delCsv) - 1; i >= 0; i-- {
		delIndex := delCsv[i]
		records = append(records[:delIndex], records[delIndex+1:]...)
	}

	addCsv := make([][]string, 0)
	for _, row := range rows {
		fromStruct, err := CsvRecordFromStruct(*row, records[0])
		if err != nil {
			return err
		}
		addCsv = append(addCsv, fromStruct)
	}

	records = append(records, addCsv...)

	var csvContent strings.Builder
	csvWriter := csv.NewWriter(&csvContent)
	if err = csvWriter.WriteAll(records); err != nil {
		log.L(ctx).Errorf("Failed to write CSV data: %v", err)

		return err
	}
	csvWriter.Flush()

	// 创建一个新的io.Reader
	csvData := strings.NewReader(csvContent.String())

	// 创建一个新的GCS writer来上传修改后的文件
	writer := client.Bucket(dataPlatformBucket).Object(objectName).NewWriter(ctx)
	if _, err = io.Copy(writer, csvData); err != nil {
		log.L(ctx).Errorf("Failed to write CSV data to GCS: %v", err)

		return err
	}

	if err = writer.Close(); err != nil {
		log.L(ctx).Errorf("Failed to close GCS writer: %v", err)

		return err
	}

	schema, _ := SchemaFromFile(viper.GetString("bq.schema"))

	// load csv to bigquery
	gcsRef := bigquery.NewGCSReference(fmt.Sprintf("gs://%s/%s", dataPlatformBucket, objectName))
	gcsRef.SourceFormat = bigquery.CSV
	gcsRef.AutoDetect = false

	gcsRef.Schema = schema
	gcsRef.FieldDelimiter = ","
	gcsRef.SkipLeadingRows = 1

	loader := bqSrv.Dataset(bqDatasetId).Table(bqTableId).LoaderFrom(gcsRef)
	loader.WriteDisposition = bigquery.WriteTruncate

	job, err := loader.Run(ctx)

	if err != nil {
		log.L(ctx).Errorf("Failed to load csv to bigquery", err)

		return err
	}

	status, err := job.Wait(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to load csv to bigquery", err)

		return err
	}

	if err := status.Err(); err != nil {
		log.L(ctx).Errorf("jobid:%s.Failed to load csv to bigquery:%v", job.ID(), err)

		return err
	}

	log.L(ctx).Infof("load csv to bigquery success")

	return nil
}

func uploadGcs(ctx context.Context, rows []*model.OrgBq) error {
	// Initialize the GCS client
	client, err := storage.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("storage.NewClient: %v", err)
	}
	defer client.Close()

	// Get a handle to the bucket and object (file)
	bucket := client.Bucket(dataPlatformBucket)
	obj := bucket.Object(fileName)

	// Open a new writer for the GCS object
	wc := obj.NewWriter(ctx)
	// It's recommended to set the "Content-Type" of the file written to GCS
	wc.ContentType = "text/csv"

	// CSV writer writes records to w (io.Writer implemented by wc)
	csvWriter := csv.NewWriter(wc)
	defer csvWriter.Flush() // Don't forget to flush the buffer!

	var headers []string
	val := reflect.ValueOf(rows[0])
	for i := 0; i < val.Type().NumField(); i++ {
		headers = append(headers, val.Type().Field(i).Name)
	}
	err = csvWriter.Write(headers)
	if err != nil {
		wc.Close()
		log.L(ctx).Errorf("csvWriter.Write: %v", err)

		return err
	}
	// Iterate over the rows to write them as CSV records
	for _, row := range rows {
		var record []string
		val := reflect.ValueOf(row)
		for i := 0; i < val.NumField(); i++ {
			field := val.Field(i)
			var str string
			switch field.Kind() {
			case reflect.String:
				str = field.String()
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				str = strconv.FormatInt(field.Int(), 10)
			case reflect.Float32, reflect.Float64:
				str = strconv.FormatFloat(field.Float(), 'f', -1, 64)
			case reflect.Bool:
				str = strconv.FormatBool(field.Bool())
			default:
				str = fmt.Sprintf("%v", field.Interface())
			}
			record = append(record, str)
		}
		err = csvWriter.Write(record)
		if err != nil {
			log.L(ctx).Errorf("csvWriter.Write: %v", err)
			wc.Close()

			return err
		}
	}

	// Check if there were any errors during flush
	if err := csvWriter.Error(); err != nil {
		wc.Close()
		return fmt.Errorf("csvWriter.Error: %v", err)
	}

	// Close the Writer when done to ensure the data is written to GCS
	if err := wc.Close(); err != nil {
		return fmt.Errorf("Writer.Close: %v", err)
	}

	fmt.Printf("Data successfully uploaded to %s/%s\n", dataPlatformBucket, fileName)
	return nil
}

func loadBqData(ctx context.Context, bqSrv *bigquery.Client, datasetID, tableID string) error {
	// Specify the GCS URI of the CSV file
	gcsRef := bigquery.NewGCSReference(fmt.Sprintf("gs://%s/%s", dataPlatformBucket, fileName))
	// Set the format of the source data, e.g., CSV, Avro, ORC, Parquet, etc.
	gcsRef.SourceFormat = bigquery.CSV // Set to bigquery.DataFormat("CSV") for CSV format

	// Specify the Load Job configuration to load the data from GCS and replace the existing data in BigQuery
	loader := bqSrv.Dataset(datasetID).Table(tableID).LoaderFrom(gcsRef)
	// Replace the table if it already exists
	loader.WriteDisposition = bigquery.WriteTruncate

	// Run the loader
	job, err := loader.Run(ctx)
	if err != nil {
		log.L(ctx).Errorf("bigquery.LoadFromGCS.Run: %v", err)

		return err
	}

	// Wait for the job to complete
	status, err := job.Wait(ctx)
	if err != nil {
		log.L(ctx).Errorf("bigquery.Job.Wait: %v", err)

		return err
	}

	// If there was an error during the job execution, it will be returned here
	if err := status.Err(); err != nil {
		log.L(ctx).Errorf("bigquery.JobStatus.Err: %v", err)

		return err
	}

	log.L(ctx).Info("Data successfully loaded from GCS and written to BigQuery")

	return nil
}

func execSql(ctx context.Context, bqSrv *bigquery.Client, sql string) error {
	job, err := bqSrv.Query(sql).Run(ctx)
	if err != nil {
		log.L(ctx).Errorf("Query.Run Error:", err)

		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		log.L(ctx).Errorf("Job.Wait Error:", err)

		return err
	}
	if status.Err() != nil {
		log.L(ctx).Errorf("Job Status Error:", status.Err())

		return status.Err()
	}
	log.L(ctx).Infof("Exec sql success: %s", sql)

	return nil
}

func insertARTableSnapshot(ctx context.Context) error {
	log.L(ctx).Info("start insertARTableSnapshot")

	mode := viper.GetString("mode")
	var bqTransService *bigquerydatatransfer.Service
	var err error

	if mode == "dev" {
		saPath := viper.GetString("bq.sa")
		bqTransService, err = bigquerydatatransfer.NewService(ctx, option.WithCredentialsFile(saPath))
	} else {
		bqTransService, err = bigquerydatatransfer.NewService(ctx)
	}
	if err != nil {
		log.L(ctx).Errorf("error creating BQ transfer client: %v", err)

		return err
	}

	scheduleQueryID := "projects/238788943257/locations/us-west2/transferConfigs/653c78ab-0000-231e-a689-24058881fbd0"

	// end with 'Z'
	startRunTime := time.Now().Format("2006-01-02T15:04:05Z")
	// 创建手动运行请求
	runRequest := &bigquerydatatransfer.StartManualTransferRunsRequest{
		RequestedRunTime: startRunTime,
	}

	resp, err := bqTransService.Projects.Locations.TransferConfigs.StartManualRuns(scheduleQueryID, runRequest).Do()
	if err != nil {
		log.L(ctx).Errorf("error starting manual transfer runs: %v", err)

		return err
	}

	runID := resp.Runs[0].Name
	// 轮询查询状态
	for {
		run, err := bqTransService.Projects.Locations.TransferConfigs.Runs.Get(runID).Do()
		if err != nil {
			log.L(ctx).Errorf("Failed to get transfer run status: %v", err)

			return err
		}

		// 检查查询状态
		switch run.State {
		case "SUCCEEDED":
			log.L(ctx).Infof("Query succeeded!")

			break
		case "FAILED":
			log.L(ctx).Errorf("Query failed: %v", run.ErrorStatus)

			return errors.New("query failed: " + run.ErrorStatus.Message)
		case "PENDING", "RUNNING":
			log.L(ctx).Infof("Query is still running...")
		default:
			log.L(ctx).Infof("Unknown query state: %v", run.State)
		}

		if run.State == "SUCCEEDED" {
			break
		}

		// 等待一段时间后再次检查
		time.Sleep(20 * time.Second)
	}

	return nil
}

func loadBqARTable(ctx context.Context) error {
	log.L(ctx).Info("start loadBqARTable")

	mode := viper.GetString("mode")

	var bqService *bqv2.Service
	var err error

	if mode == "dev" {
		saPath := viper.GetString("bq.sa")
		bqService, err = bqv2.NewService(ctx, option.WithCredentialsFile(saPath))
	} else {
		bqService, err = bqv2.NewService(ctx)
	}
	if err != nil {
		log.L(ctx).Errorf("error creating BQ client: %v", err)

		return err
	}

	arGcsPath := viper.GetString("gcs.arPath")
	schema, err := arSchema()
	if err != nil {
		log.L(ctx).Errorf("error arSchema: %v", err)

		return err
	}
	resp, err := bqService.Jobs.Insert(projectID, &bqv2.Job{
		Configuration: &bqv2.JobConfiguration{
			Load: &bqv2.JobConfigurationLoad{
				SourceUris: []string{"gs://" + dataPlatformBucket + "/" + arGcsPath},
				DestinationTable: &bqv2.TableReference{
					ProjectId: projectID,
					DatasetId: datasetID,
					TableId:   "ar_table",
				},
				WriteDisposition: "WRITE_TRUNCATE",
				SourceFormat:     "CSV",
				Schema: &bqv2.TableSchema{
					Fields: schema,
				},
				AllowJaggedRows:     true,
				AllowQuotedNewlines: true,
				IgnoreUnknownValues: true,
				SkipLeadingRows:     1,
			},
		},
	}).Do()
	if err != nil {
		log.L(ctx).Errorf("error loading HandleARTable to BQ: %v", err)

		return err
	}

	// check job status
	err = checkBqJobStatus(ctx, bqService, resp.JobReference.JobId)
	if err != nil {
		log.L(ctx).Errorf("error checking job status: %v", err)

		return err
	}

	log.L(ctx).Info("end loadBqARTable")

	return nil
}

func checkBqJobStatus(ctx context.Context, bqService *bqv2.Service, jobID string) error {
	for {
		job, err := bqService.Jobs.Get(projectID, jobID).Location("us-west2").Do()
		if err != nil {
			log.L(ctx).Errorf("error getting job status: %v", err)

			return err
		}

		if job.Status.State == "DONE" {
			if job.Status.ErrorResult != nil {
				log.L(ctx).Errorf("job failed: %v", job.Status.ErrorResult.Message)

				return errors.New("job failed: " + job.Status.ErrorResult.Message + " " + job.Status.ErrorResult.Reason)
			}
			break
		}

		time.Sleep(3 * time.Second)
	}

	return nil
}

func arSchema() ([]*bqv2.TableFieldSchema, error) {

	schemaPath := viper.GetString("schema")

	// 检查 schemaPath 是否为空
	if schemaPath == "" {
		return nil, errors.New("schema path is empty")
	}

	// 读取 JSON 文件
	data, err := os.ReadFile(schemaPath)
	if err != nil {
		return nil, err
	}

	// 使用 json.RawMessage 解析 JSON 的原始字节
	var rawMap map[string]json.RawMessage
	if err := json.Unmarshal(data, &rawMap); err != nil {
		return nil, err
	}

	// 创建 schema 切片
	schema := make([]*bqv2.TableFieldSchema, 0, len(rawMap))

	// 使用 gjson 解析 JSON
	result := gjson.ParseBytes(data)
	result.ForEach(func(key, value gjson.Result) bool {
		schema = append(schema, &bqv2.TableFieldSchema{
			Name: key.String(),
			Type: value.String(),
		})
		return true // 继续遍历
	})

	return schema, nil
}

func GetInvoiceCollection(ctx context.Context) (map[string]string, error) {
	bqSrv, err := bigquery.NewClient(ctx, GcpProject)
	if err != nil {
		log.L(ctx).Errorf("Failed to create bigquery client: %v", err)

		return nil, err
	}

	query := `SELECT id, name FROM gcp-digital-bi.salesforce.invoice_collection_c where is_deleted = false`

	q := bqSrv.Query(query)
	it, err := q.Read(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to query bigquery: %v", err)

		return nil, err
	}

	result := make(map[string]string)

	for {
		var values []bigquery.Value
		err := it.Next(&values)
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.L(ctx).Errorf("Failed to read bigquery result: %v", err)

			return nil, err
		}
		result[values[1].(string)] = values[0].(string)
	}

	return result, nil
}

// fetchRevenueOrderData fetches order/revrec data from the revrec_order_test table
func fetchRevenueOrderData(ctx context.Context, bqSrv *bigquery.Client, billMonth string) ([]*model.RevenueOrderData, error) {
	log.L(ctx).Info("fetching revenue order data from revrec_order_test")

	querySql := "SELECT * FROM `gcp-digital-bi-dev.sandbox_dwh_ywang.monthly_revrec_orders` WHERE bill_month = '" + billMonth + "'"

	query := bqSrv.Query(querySql)
	it, err := query.Read(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to read query: %v", err)
		return nil, err
	}

	var results []*model.RevenueOrderData
	for {
		var record model.RevenueOrderData
		err := it.Next(&record)
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.L(ctx).Errorf("Failed to read row: %v", err)
			return nil, err
		}
		results = append(results, &record)
	}

	log.L(ctx).Infof("fetched %d revenue order records", len(results))
	return results, nil
}

// fetchCollectionData fetches collection data from the revrec_org_test table
func fetchCollectionData(ctx context.Context, bqSrv *bigquery.Client, billMonth string) ([]*model.OrgCollectionData, error) {
	log.L(ctx).Info("fetching collection data from revrec_org_test")

	querySql := "SELECT * FROM `gcp-digital-bi-dev.sandbox_dwh_ywang.monthly_revrec_collections` WHERE bill_month = '" + billMonth + "'"

	query := bqSrv.Query(querySql)
	it, err := query.Read(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to read query: %v", err)
		return nil, err
	}

	var results []*model.OrgCollectionData
	for {
		var record model.OrgCollectionData
		err := it.Next(&record)
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.L(ctx).Errorf("Failed to read row: %v", err)
			return nil, err
		}
		results = append(results, &record)
	}

	log.L(ctx).Infof("fetched %d collection records", len(results))
	return results, nil
}

// mergeRevenueAndCollectionData merges revenue order data and collection data into OrgBq format
func mergeRevenueAndCollectionData(ctx context.Context, revenueData []*model.RevenueOrderData, collectionData []*model.OrgCollectionData) ([]*model.OrgBq, error) {
	log.L(ctx).Info("merging revenue and collection data")

	// Create a map for collection data by tenant_id for efficient lookup
	collectionMap := make(map[string]*model.OrgCollectionData)
	for _, collection := range collectionData {
		if collection.AdjustTenantID.Valid {
			collectionMap[collection.AdjustTenantID.StringVal] = collection
		}
	}

	var mergedResults []*model.OrgBq
	for _, revenue := range revenueData {
		orgBq := &model.OrgBq{
			BillMonth:        revenue.BillMonth,
			AdjustTenantID:   bigquery.NullString{StringVal: revenue.TenantID.StringVal, Valid: revenue.TenantID.Valid},
			OriginalTenantID: bigquery.NullString{StringVal: revenue.TenantID.StringVal, Valid: revenue.TenantID.Valid},
			AccountID:        revenue.AccountID,
			AccountName:      revenue.AccountName,
			StripeTaxRate:    revenue.StripeTaxRate,
			CollectionsFrom:  revenue.CollectionsFrom,
			OrderNumber:      revenue.OrderNumber,

			// Map field names according to requirements:
			// new_order_start_date -> order_start_date
			// new_order_end_date -> order_end_date
			// new_order_amount -> revenue_amount
			NewOrderStartDate: revenue.OrderStartDate,
			NewOrderEndDate:   revenue.OrderEndDate,
			NewOrderAmount:    revenue.RevenueAmount,

			// Map revenue amounts
			RevenueAmount:          revenue.RevenueAmount,
			DedicatedRevenueAmount: revenue.DedicatedRevenueAmount,
			StarterRevenueAmount:   revenue.StarterRevenueAmount,
			SupportRevenueAmount:   revenue.SupportRevenueAmount,
			EssentialRevenueAmount: revenue.EssentialRevenueAmount,
			CreditAmount:           revenue.CreditAmount,

			// Map adjusted amounts
			AdjustedRevenueAmount:   revenue.AdjustedRevenueAmount,
			AdjustedDedicatedAmount: revenue.AdjustedDedicatedAmount,
			AdjustedStarterAmount:   revenue.AdjustedStarterAmount,
			AdjustedSupportAmount:   revenue.AdjustedSupportAmount,
			AdjustedEssentialAmount: revenue.AdjustedEssentialAmount,
			AdjustedShortfallAmount: revenue.AdjustedShortfallAmount,

			// Set need_new_order based on is_new_order field
			NeedNewOrder: bigquery.NullString{
				StringVal: func() string {
					if revenue.IsNewOrder.Valid && revenue.IsNewOrder.Bool {
						return "yes"
					}
					return "no"
				}(),
				Valid: true,
			},
		}

		// Merge collection data if available for this tenant
		if collection, exists := collectionMap[revenue.TenantID.StringVal]; exists {
			orgBq.StartDate = collection.StartDate
			orgBq.EndDate = collection.EndDate
			orgBq.StripeAmountIncludingTax = collection.StripeAmountIncludingTax
			orgBq.StripeInvoiceNumber = collection.StripeInvoiceNumber
			orgBq.StripeInvoice = collection.StripeInvoice
			orgBq.StripeInvoicePaid = collection.StripeInvoicePaid
			orgBq.StripeInvoiceFinalizedDate = collection.StripeInvoiceFinalizedDate
			orgBq.StripeInvoicePaidDate = collection.StripeInvoicePaidDate
		}

		mergedResults = append(mergedResults, orgBq)
	}

	log.L(ctx).Infof("merged data into %d OrgBq records", len(mergedResults))
	return mergedResults, nil
}
