package system

import (
	"archive/zip"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	subscribe_dto "github.com/APIParkLab/APIPark/module/subscribe/dto"

	"github.com/eolinker/eosc/log"

	ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"

	"github.com/APIParkLab/APIPark/model/plugin_model"
	"github.com/APIParkLab/APIPark/service/api"

	ai_api "github.com/APIParkLab/APIPark/module/ai-api"

	model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime"
	ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
	router_dto "github.com/APIParkLab/APIPark/module/router/dto"

	"github.com/eolinker/go-common/store"

	"github.com/APIParkLab/APIPark/module/ai"

	catalogue_dto "github.com/APIParkLab/APIPark/module/catalogue/dto"

	application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto"
	service_dto "github.com/APIParkLab/APIPark/module/service/dto"
	team_dto "github.com/APIParkLab/APIPark/module/team/dto"
	"github.com/eolinker/go-common/register"
	"github.com/eolinker/go-common/server"
	"github.com/eolinker/go-common/utils"
	"github.com/google/uuid"

	system_dto "github.com/APIParkLab/APIPark/module/system/dto"

	application_authorization "github.com/APIParkLab/APIPark/module/application-authorization"
	"github.com/APIParkLab/APIPark/module/catalogue"
	"github.com/APIParkLab/APIPark/module/router"
	"github.com/APIParkLab/APIPark/module/service"
	"github.com/APIParkLab/APIPark/module/subscribe"
	"github.com/APIParkLab/APIPark/module/system"
	"github.com/APIParkLab/APIPark/module/team"
	"github.com/APIParkLab/APIPark/module/upstream"
	"github.com/gin-gonic/gin"
)

var _ IExportConfigController = (*imlExportConfigController)(nil)

type imlExportConfigController struct {
	teamModule                     team.ITeamExportModule                               `autowired:""`
	serviceModule                  service.IExportServiceModule                         `autowired:""`
	appModule                      service.IExportAppModule                             `autowired:""`
	routerModule                   router.IExportRouterModule                           `autowired:""`
	upstreamModule                 upstream.IExportUpstreamModule                       `autowired:""`
	applicationAuthorizationModule application_authorization.IExportAuthorizationModule `autowired:""`
	catalogueModule                catalogue.IExportCatalogueModule                     `autowired:""`
	subscribeModule                subscribe.IExportSubscribeModule                     `autowired:""`
	applyModule                    subscribe.IExportSubscribeApprovalModule             `autowired:""`
}

type ExportFile struct {
	Driver string      `json:"driver"`
	Data   interface{} `json:"data"`
}

func (e *ExportFile) Byte() []byte {
	b, _ := json.Marshal(e.Data)
	return b
}

func zipFile(files []*ExportFile) (*bytes.Buffer, error) {
	// 创建一个缓冲区用于存储 ZIP 文件内容
	buf := new(bytes.Buffer)
	zipWriter := zip.NewWriter(buf)
	now := time.Now()
	// 将文件写入 ZIP
	for _, file := range files {
		header := &zip.FileHeader{
			Name:     fmt.Sprintf("%s.json", file.Driver),
			Method:   zip.Deflate,
			Modified: now,
		}
		f, err := zipWriter.CreateHeader(header)
		if err != nil {
			return nil, fmt.Errorf("failed to create zip file: %v", err)
		}
		_, err = f.Write(file.Byte())
		if err != nil {
			return nil, fmt.Errorf("failed to write to zip file: %v", err)
		}
	}

	// 关闭 ZIP writer，完成压缩过程
	err := zipWriter.Close()
	if err != nil {
		return nil, fmt.Errorf("failed to close zip writer: %v", err)
	}
	return buf, nil
}

func (i *imlExportConfigController) ExportAll(ctx *gin.Context) error {
	files, err := i.appendFiles(ctx)
	if err != nil {
		return err
	}
	buf, err := zipFile(files)
	if err != nil {

	}

	ctx.DataFromReader(http.StatusOK, int64(buf.Len()), "application/zip", buf, map[string]string{
		"Content-Disposition": "attachment; filename=\"export.zip\"",
	})
	return nil
}

func (i *imlExportConfigController) appendFiles(ctx *gin.Context) ([]*ExportFile, error) {
	type exportConfig struct {
		exportFunc func(ctx *gin.Context) (interface{}, error)
		driver     string
	}

	exports := []exportConfig{
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.teamModule.ExportAll(ctx)
			},
			driver: "team",
		},
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.serviceModule.ExportAll(ctx)
			},
			driver: "service",
		},
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.appModule.ExportAll(ctx)
			},
			driver: "app",
		},
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.routerModule.ExportAll(ctx)
			},
			driver: "api",
		},
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.upstreamModule.ExportAll(ctx)
			},
			driver: "upstream",
		},
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.applicationAuthorizationModule.ExportAll(ctx)
			},
			driver: "authorization",
		},
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.catalogueModule.ExportAll(ctx)
			},
			driver: "catalogue",
		},
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.subscribeModule.ExportAll(ctx)
			},
			driver: "subscribe",
		},
		{
			exportFunc: func(ctx *gin.Context) (interface{}, error) {
				return i.applyModule.ExportAll(ctx)
			},
			driver: "apply",
		},
	}

	files := make([]*ExportFile, 0, len(exports))
	for _, config := range exports {
		data, err := config.exportFunc(ctx)
		if err != nil {
			return nil, fmt.Errorf("[%s] failed to export data: %v", config.driver, err)
		}

		files = append(files, &ExportFile{
			Driver: config.driver,
			Data:   data,
		})
	}

	return files, nil
}

var (
	_ ISettingController = (*imlSettingController)(nil)
)

type imlSettingController struct {
	settingModule system.ISettingModule `autowired:""`
}

func (i *imlSettingController) Get(ctx *gin.Context) (*system_dto.Setting, error) {
	return i.settingModule.Get(ctx), nil
}

func (i *imlSettingController) Set(ctx *gin.Context, input *system_dto.InputSetting) error {
	return i.settingModule.Set(ctx, input)
}

type imlInitController struct {
	teamModule                     team.ITeamModule                               `autowired:""`
	appModule                      service.IAppModule                             `autowired:""`
	serviceModule                  service.IServiceModule                         `autowired:""`
	applicationAuthorizationModule application_authorization.IAuthorizationModule `autowired:""`
	catalogueModule                catalogue.ICatalogueModule                     `autowired:""`
	providerModule                 ai.IProviderModule                             `autowired:""`
	subscribeModule                subscribe.ISubscribeModule                     `autowired:""`
	transaction                    store.ITransaction                             `autowired:""`
	aiAPIModule                    ai_api.IAPIModule                              `autowired:""`
	docModule                      service.IServiceDocModule                      `autowired:""`
	routerModule                   router.IRouterModule                           `autowired:""`
}

func (i *imlInitController) OnInit() {
	register.Handle(func(v server.Server) {
		ctx := utils.SetUserId(context.Background(), "admin")

		err := i.transaction.Transaction(ctx, func(ctx context.Context) error {
			teams, err := i.teamModule.Search(ctx, "")
			if err != nil {
				return fmt.Errorf("get teams error: %v", err)
			}
			if len(teams) > 0 {
				return nil
			}
			items, err := i.catalogueModule.Search(ctx, "")
			if err != nil {
				return fmt.Errorf("get catalogue error: %v", err)
			}
			catalogueId := uuid.New().String()
			if len(items) == 0 {
				err = i.catalogueModule.Create(ctx, &catalogue_dto.CreateCatalogue{
					Id:   catalogueId,
					Name: "Default Category",
				})
				if err != nil {
					return fmt.Errorf("create default catalogue error: %v", err)
				}
			} else {
				catalogueId = items[0].Id
			}

			info, err := i.teamModule.Create(ctx, &team_dto.CreateTeam{
				Name:        "Default Team",
				Description: "Auto created By APIPark",
			})
			if err != nil {
				return fmt.Errorf("create default team error: %v", err)
			}
			app, err := i.appModule.CreateApp(ctx, info.Id, &service_dto.CreateApp{
				Name:        "Demo Application",
				Description: "Auto created By APIPark",
			})
			if err != nil {
				return fmt.Errorf("create default app error: %v", err)
			}
			// 创建Rest服务
			restPath := "/rest-demo"
			serviceInfo, err := i.serviceModule.Create(ctx, info.Id, &service_dto.CreateService{
				Name:         "REST Demo Service",
				Prefix:       "/rest-demo",
				Description:  "Auto created By APIPark",
				ServiceType:  "public",
				Catalogue:    catalogueId,
				ApprovalType: "auto",
				Kind:         "rest",
			})
			if err != nil {
				return fmt.Errorf("create default service error: %v", err)
			}
			path := fmt.Sprintf("/%s/", strings.Trim(restPath, "/"))
			_, err = i.routerModule.Create(ctx, serviceInfo.Id, &router_dto.Create{
				Id:          uuid.NewString(),
				Name:        "",
				Path:        path + "*",
				Methods:     []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch, http.MethodOptions},
				Description: "auto create by create service",
				Protocols:   []string{"http", "https"},
				MatchRules:  nil,
				Upstream:    "",
				Proxy: &router_dto.InputProxy{
					Path:    path,
					Timeout: 30000,
					Retry:   0,
				},
				Disable: false,
			})
			if err != nil {
				return fmt.Errorf("create default router error: %v", err)
			}
			err = i.subscribeModule.AddSubscriber(ctx, serviceInfo.Id, &subscribe_dto.AddSubscriber{
				Application: app.Id,
			})
			if err != nil {
				return err
			}

			// 创建AI服务
			err = i.createAIService(ctx, info.Id, &service_dto.CreateService{
				Name:         "AI Demo Service",
				Prefix:       "/ai-demo",
				Description:  "Auto created By APIPark",
				ServiceType:  "public",
				Catalogue:    catalogueId,
				ApprovalType: "auto",
				Kind:         "ai",
			}, app.Id)
			if err != nil {
				return err
			}

			_, err = i.applicationAuthorizationModule.AddAuthorization(ctx, app.Id, &application_authorization_dto.CreateAuthorization{
				Name:       "Default API Key",
				Driver:     "apikey",
				Position:   "Header",
				TokenName:  "Authorization",
				ExpireTime: 0,
				Config: map[string]interface{}{
					"apikey": uuid.New().String(),
				},
			})
			if err != nil {
				return fmt.Errorf("create default api key error: %v", err)
			}
			return nil
		})
		if err != nil {
			log.Errorf("init iml error: %v", err)
		}
	})
}
func (i *imlInitController) createAIService(ctx context.Context, teamID string, input *service_dto.CreateService, appId string) error {

	providerId := "fakegpt"
	err := i.providerModule.UpdateProviderConfig(ctx, providerId, &ai_dto.UpdateConfig{
		Config: "{\n  \"apikey\": \"xxx\" \n}",
	})
	if err != nil {
		return fmt.Errorf("update %s config error: %v", providerId, err)
	}
	input.Provider = &providerId
	if input.Id == "" {
		input.Id = uuid.New().String()
	}
	providerInfo, err := i.providerModule.Provider(ctx, *input.Provider)
	if err != nil {
		return err
	}
	input.Model = &providerInfo.DefaultLLM

	if input.Prefix == "" {
		if len(input.Id) < 9 {
			input.Prefix = input.Id
		} else {
			input.Prefix = input.Id[:8]
		}
	}
	pv, err := i.providerModule.Provider(ctx, *input.Provider)
	if err != nil {
		return err
	}
	p, has := model_runtime.GetProvider(*input.Provider)
	if !has {
		return fmt.Errorf("provider not found")
	}
	m, has := p.GetModel(pv.DefaultLLM)
	if !has {
		return fmt.Errorf("model %s not found", pv.DefaultLLM)
	}

	var info *service_dto.Service
	err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
		var err error
		info, err = i.serviceModule.Create(ctx, teamID, input)
		if err != nil {
			return err
		}
		path := fmt.Sprintf("/%s/chat/completions", strings.Trim(input.Prefix, "/"))
		timeout := 300000
		retry := 0
		aiPrompt := &ai_api_dto.AiPrompt{
			//Variables: []*ai_api_dto.AiPromptVariable{
			//	{
			//		Key:         "source_lang",
			//		Description: "",
			//		Require:     true,
			//	},
			//	{
			//		Key:         "target_lang",
			//		Description: "",
			//		Require:     true,
			//	},
			//	{
			//		Key:         "text",
			//		Description: "",
			//		Require:     true,
			//	},
			//},
			//Prompt: "You need to translate {{source_lang}} into {{target_lang}}, and the following is the content that needs to be translated.\n---\n{{text}}",
		}
		aiModel := &ai_api_dto.AiModel{
			Id:       m.ID(),
			Config:   m.DefaultConfig(),
			Provider: providerId,
		}
		name := "Demo Chat API"
		description := "A demo that shows you how to use a prompt to create a Chat API."
		apiId := uuid.New().String()
		err = i.aiAPIModule.Create(
			ctx,
			info.Id,
			&ai_api_dto.CreateAPI{
				Id:          apiId,
				Name:        name,
				Path:        path,
				Description: description,
				Disable:     false,
				AiPrompt:    aiPrompt,
				AiModel:     aiModel,
				Timeout:     timeout,
				Retry:       retry,
			},
		)
		if err != nil {
			return err
		}
		plugins := make(map[string]api.PluginSetting)
		plugins["ai_prompt"] = api.PluginSetting{
			Config: plugin_model.ConfigType{
				"prompt":    aiPrompt.Prompt,
				"variables": aiPrompt.Variables,
			},
		}
		plugins["ai_formatter"] = api.PluginSetting{
			Config: plugin_model.ConfigType{
				"model":    aiModel.Id,
				"provider": info.Provider.Id,
				"config":   aiModel.Config,
			},
		}
		_, err = i.routerModule.Create(ctx, info.Id, &router_dto.Create{
			Id:   apiId,
			Name: name,
			Path: path,
			Methods: []string{
				http.MethodPost,
			},
			Description: description,
			Protocols:   []string{"http", "https"},
			MatchRules:  nil,
			Proxy: &router_dto.InputProxy{
				Path:    path,
				Timeout: timeout,
				Retry:   retry,
				Plugins: plugins,
			},
			Disable: false,
			//Upstream: info.Provider.Id,
		})
		if err != nil {
			return err
		}
		err = i.subscribeModule.AddSubscriber(ctx, info.Id, &subscribe_dto.AddSubscriber{
			Application: appId,
		})
		if err != nil {
			return err
		}

		return i.docModule.SaveServiceDoc(ctx, info.Id, &service_dto.SaveServiceDoc{
			Doc: "The Translation API allows developers to translate text from one language to another. It supports multiple languages and enables easy integration of high-quality translation features into applications. With simple API requests, you can quickly translate content into different target languages.",
		})
	})

	return err
}
