package main

import (
	"fmt"
	"log"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/shopspring/decimal"
	"go.uber.org/zap"

	"rmser/config"
	"rmser/internal/domain/catalog"
	"rmser/internal/domain/invoices"
	"rmser/internal/infrastructure/db"
	"rmser/internal/infrastructure/ocr_client"
	tgBot "rmser/internal/transport/telegram"

	// Репозитории (инфраструктура)
	catalogPkg "rmser/internal/infrastructure/repository/catalog"
	invoicesPkg "rmser/internal/infrastructure/repository/invoices"
	ocrRepoPkg "rmser/internal/infrastructure/repository/ocr"
	opsRepoPkg "rmser/internal/infrastructure/repository/operations"
	recipesPkg "rmser/internal/infrastructure/repository/recipes"
	recRepoPkg "rmser/internal/infrastructure/repository/recommendations"

	"rmser/internal/infrastructure/rms"
	invServicePkg "rmser/internal/services/invoices" // Сервис накладных
	ocrServicePkg "rmser/internal/services/ocr"
	recServicePkg "rmser/internal/services/recommend"
	"rmser/internal/services/sync"
	"rmser/internal/transport/http/handlers" // Хендлеры
	"rmser/pkg/logger"
)

func main() {
	// 1. Загрузка конфигурации
	cfg, err := config.LoadConfig(".")
	if err != nil {
		log.Fatalf("Ошибка загрузки конфига: %v", err)
	}
	// OCR Client
	pyClient := ocr_client.NewClient(cfg.OCR.ServiceURL)

	// 2. Инициализация логгера
	logger.Init(cfg.App.Mode)
	defer logger.Log.Sync()

	logger.Log.Info("Запуск приложения rmser", zap.String("mode", cfg.App.Mode))

	// 3a. Подключение Redis (Новое)
	// redisClient, err := redis.NewClient(cfg.Redis.Addr, cfg.Redis.Password, cfg.Redis.DB)
	// if err != nil {
	// 	logger.Log.Fatal("Ошибка подключения к Redis", zap.Error(err))
	// }

	// 3. Подключение к БД (PostgreSQL)
	database := db.NewPostgresDB(cfg.DB.DSN)

	// 4. Инициализация слоев
	rmsClient := rms.NewClient(cfg.RMS.BaseURL, cfg.RMS.Login, cfg.RMS.Password)
	catalogRepo := catalogPkg.NewRepository(database)
	recipesRepo := recipesPkg.NewRepository(database)
	invoicesRepo := invoicesPkg.NewRepository(database)
	opsRepo := opsRepoPkg.NewRepository(database)
	recRepo := recRepoPkg.NewRepository(database)
	ocrRepo := ocrRepoPkg.NewRepository(database)

	syncService := sync.NewService(rmsClient, catalogRepo, recipesRepo, invoicesRepo, opsRepo)
	recService := recServicePkg.NewService(recRepo)
	ocrService := ocrServicePkg.NewService(ocrRepo, catalogRepo, pyClient)
	invoiceService := invServicePkg.NewService(rmsClient)
	invoiceHandler := handlers.NewInvoiceHandler(invoiceService)

	// --- БЛОК ПРОВЕРКИ СИНХРОНИЗАЦИИ (Run-once on start) ---
	go func() {
		logger.Log.Info(">>> Запуск тестовой синхронизации...")

		// 1. Каталог
		if err := syncService.SyncCatalog(); err != nil {
			logger.Log.Error("Ошибка синхронизации каталога", zap.Error(err))
		} else {
			logger.Log.Info("<<< Каталог успешно синхронизирован")
		}

		// 2. Техкарты
		if err := syncService.SyncRecipes(); err != nil {
			logger.Log.Error("Ошибка синхронизации техкарт", zap.Error(err))
		} else {
			logger.Log.Info("<<< Техкарты успешно синхронизированы")
		}

		// 3. Накладные
		if err := syncService.SyncInvoices(); err != nil {
			logger.Log.Error("Ошибка синхронизации накладных", zap.Error(err))
		} else {
			logger.Log.Info("<<< Накладные успешно синхронизированы")
		}
		// 4. Складские операции
		if err := syncService.SyncStoreOperations(); err != nil {
			logger.Log.Error("Ошибка синхронизации операций", zap.Error(err))
		} else {
			logger.Log.Info("<<< Операции успешно синхронизированы")
		}
		logger.Log.Info(">>> Запуск расчета рекомендаций...")
		if err := recService.RefreshRecommendations(); err != nil {
			logger.Log.Error("Ошибка расчета рекомендаций", zap.Error(err))
		} else {
			// Для отладки можно вывести пару штук
			recs, _ := recService.GetRecommendations()
			logger.Log.Info("<<< Анализ завершен", zap.Int("found", len(recs)))
		}
		// === ТЕСТ ОТПРАВКИ НАКЛАДНОЙ ===
		// Запускаем через небольшую паузу, чтобы логи не перемешались
		time.Sleep(2 * time.Second)
		runManualInvoiceTest(rmsClient, catalogRepo)
		// ===============================
	}()
	// -------------------------------------------------------
	// Запуск бота (в отдельной горутине, т.к. Start() блокирует поток)
	if cfg.Telegram.Token != "" {
		bot, err := tgBot.NewBot(cfg.Telegram, ocrService)
		if err != nil {
			logger.Log.Fatal("Ошибка создания Telegram бота", zap.Error(err))
		}
		go bot.Start()
		defer bot.Stop() // Graceful shutdown
	} else {
		logger.Log.Warn("Telegram token не задан, бот не запущен")
	}
	// 5. Запуск HTTP сервера (Gin)
	if cfg.App.Mode == "release" {
		gin.SetMode(gin.ReleaseMode)
	}
	r := gin.Default()

	api := r.Group("/api")
	{
		// ... другие роуты ...
		api.POST("/invoices/send", invoiceHandler.SendInvoice)
	}

	// Простой хелсчек
	r.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"status": "ok",
			"time":   time.Now().Format(time.RFC3339),
		})
	})

	logger.Log.Info("Сервер запускается", zap.String("port", cfg.App.Port))
	if err := r.Run(":" + cfg.App.Port); err != nil {
		logger.Log.Fatal("Ошибка запуска сервера", zap.Error(err))
	}
}

// runManualInvoiceTest создает тестовую накладную и отправляет её в RMS
func runManualInvoiceTest(client rms.ClientI, catRepo catalog.Repository) {
	logger.Log.Info(">>> [TEST] Начало теста создания накладной...")

	// === НАСТРОЙКИ ТЕСТА ===
	const (
		MyStoreGUID    = "1239d270-1bbe-f64f-b7ea-5f00518ef508" // <-- ВАШ СКЛАД
		MySupplierGUID = "780aa87e-2688-4f99-915b-7924ca392ac1" // <-- ВАШ ПОСТАВЩИК
		MyProductGUID  = "607a1e96-f539-45d2-8709-3919f94bdc3e" // <-- ВАШ ТОВАР (Опционально)
	)
	// =======================

	// 1. Поиск товара
	var targetProduct catalog.Product
	var found bool

	// Если задан конкретный ID товара в константе - ищем его
	if MyProductGUID != "00000000-0000-0000-0000-000000000000" {
		products, _ := catRepo.GetAll() // В реальном коде лучше GetByID, но у нас repo пока простой
		targetUUID := uuid.MustParse(MyProductGUID)
		for _, p := range products {
			if p.ID == targetUUID {
				targetProduct = p
				found = true
				break
			}
		}
		if !found {
			logger.Log.Error("[TEST] Товар с указанным MyProductGUID не найден в локальной БД")
			return
		}
	} else {
		// Иначе ищем первый попавшийся GOODS
		products, err := catRepo.GetAll()
		if err != nil || len(products) == 0 {
			logger.Log.Error("[TEST] БД пуста. Выполните SyncCatalog.")
			return
		}

		for _, p := range products {
			// Ищем именно товар (GOODS) или заготовку (PREPARED), но не группу и не блюдо, если нужно
			if p.Type == "GOODS" {
				targetProduct = p
				found = true
				break
			}
		}
		if !found {
			logger.Log.Warn("[TEST] Не найдено товаров с типом GOODS. Берем первый попавшийся.")
			targetProduct = products[0]
		}
	}

	logger.Log.Info("[TEST] Используем товар",
		zap.String("name", targetProduct.Name),
		zap.String("type", targetProduct.Type),
		zap.String("uuid", targetProduct.ID.String()),
	)

	// 2. Парсинг ID Склада и Поставщика
	storeID := uuid.Nil
	if MyStoreGUID != "00000000-0000-0000-0000-000000000000" {
		storeID = uuid.MustParse(MyStoreGUID)
	} else {
		logger.Log.Warn("[TEST] ID склада не задан! iiko вернет ошибку валидации.")
		storeID = uuid.New() // Рандом, чтобы XML собрался
	}

	supplierID := uuid.Nil
	if MySupplierGUID != "00000000-0000-0000-0000-000000000000" {
		supplierID = uuid.MustParse(MySupplierGUID)
	} else {
		logger.Log.Warn("[TEST] ID поставщика не задан!")
		supplierID = uuid.New()
	}

	// 3. Формируем накладную
	testInv := invoices.Invoice{
		ID:             uuid.Nil,
		DocumentNumber: fmt.Sprintf("TEST-%d", time.Now().Unix()%10000),
		DateIncoming:   time.Now(),
		SupplierID:     supplierID,
		DefaultStoreID: storeID,
		Status:         "NEW",
		Items: []invoices.InvoiceItem{
			{
				ProductID: targetProduct.ID,
				Amount:    decimal.NewFromFloat(5.0),
				Price:     decimal.NewFromFloat(100.0),
				Sum:       decimal.NewFromFloat(500.0),
			},
		},
	}

	// 4. Отправляем
	logger.Log.Info("[TEST] Отправка запроса в RMS...")
	docNum, err := client.CreateIncomingInvoice(testInv)
	if err != nil {
		logger.Log.Error("[TEST] Ошибка отправки накладной", zap.Error(err))
	} else {
		logger.Log.Info("[TEST] УСПЕХ! Накладная создана.", zap.String("doc_number", docNum))
	}
}
