package controllers

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"nearle/models"
	"nearle/services"
	"net/http"
	"strconv"

	"github.com/gofiber/fiber/v2"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

type UtilsController struct {
	utilsService services.UtilsService
}

func NewUtilsController(utilsService services.UtilsService) *UtilsController {
	return &UtilsController{utilsService: utilsService}
}

const (
	fcmURL     = "https://fcm.googleapis.com/v1/projects/nearle-gear/messages:send"
	scope      = "https://www.googleapis.com/auth/firebase.messaging"
	serviceAcc = "nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json" // Path to your service account JSON
)

var client *http.Client

func (ctl *UtilsController) GetAppTypes(c *fiber.Ctx) error {

	tag := c.Query("tag")

	result, err := ctl.utilsService.GetApptypes(tag)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": err.Error(),
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": result,
	})

}

func (ctl *UtilsController) NotifyUser(c *fiber.Ctx) error {
	var body struct {
		Token        string                 `json:"token"`
		Notification models.FcmNotification `json:"notification"`
		Data         map[string]string      `json:"data"`
	}

	if err := c.BodyParser(&body); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"code":    http.StatusBadRequest,
			"message": fmt.Sprintf("Error parsing request body: %v", err),
			"status":  false,
		})
	}

	err := ctl.utilsService.SendNotification(body.Token, body.Notification, body.Data)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": fmt.Sprintf("Error sending notification: %v", err),
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "FCM message sent successfully!",
		"status":  true,
	})
}

func (ctl *UtilsController) GetSubcategories(c *fiber.Ctx) error {
	mid, _ := strconv.Atoi(c.Query("moduleid"))
	cid, _ := strconv.Atoi(c.Query("categoryid"))

	result, err := ctl.utilsService.GetSubcategories(mid, cid)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": err.Error(),
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": result,
	})
}

func (ctl *UtilsController) GetApplocations(c *fiber.Ctx) error {
	aid, err := strconv.Atoi(c.Query("applocationid"))
	if err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{
			"code":    http.StatusBadRequest,
			"message": "Invalid applocationid",
			"status":  false,
		})
	}

	result, err := ctl.utilsService.GetApplocations(aid)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": err.Error(),
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"status":  true,
		"code":    http.StatusOK,
		"message": "Successful",
		"details": result,
	})
}

func (ctl *UtilsController) GetApplocationConfig(c *fiber.Ctx) error {
	aid, err := strconv.Atoi(c.Query("applocationid"))
	if err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{
			"code":    http.StatusBadRequest,
			"message": "Invalid applocationid",
			"status":  false,
		})
	}

	result, err := ctl.utilsService.GetApplocationConfig(aid)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    http.StatusInternalServerError,
			"message": err.Error(),
			"status":  false,
		})
	}

	return c.JSON(fiber.Map{
		"status":  true,
		"code":    http.StatusOK,
		"message": "Successful",
		"details": result,
	})
}

type Notification struct {
	Title string `json:"title"`
	Body  string `json:"body"`
}

type FCMRequestBody struct {
	Message struct {
		Notification Notification `json:"notification"`
		Token        string       `json:"token"`
	} `json:"message"`
}

func GetClientFromServiceAccount() (*http.Client, error) {
	data, err := ioutil.ReadFile(serviceAcc)
	if err != nil {
		return nil, fmt.Errorf("failed to read service account file: %v", err)
	}

	config, err := google.JWTConfigFromJSON(data, scope)
	if err != nil {
		return nil, fmt.Errorf("failed to parse service account JSON: %v", err)
	}

	
	client := config.Client(oauth2.NoContext)
	return client, nil
}

func sendFCMMessage(token, title, body string) error {
	if client == nil {
		var err error
		client, err = GetClientFromServiceAccount()
		if err != nil {
			return fmt.Errorf("failed to get client: %v", err)
		}
	}

	notification := Notification{
		Title: title,
		Body:  body,
	}

	fcmRequest := FCMRequestBody{}
	fcmRequest.Message.Notification = notification
	fcmRequest.Message.Token = token

	requestBody, err := json.Marshal(fcmRequest)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", fcmURL, bytes.NewBuffer(requestBody))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := ioutil.ReadAll(resp.Body)
		return fmt.Errorf("failed to send message: %s", string(bodyBytes))
	}

	return nil
}

func (ctl *UtilsController) NotifyAdmin(c *fiber.Ctx) error {
	body := new(struct {
		Token        []string `json:"token"`
		Notification struct {
			Title string `json:"title"`
			Body  string `json:"body"`
			Sound string `json:"sound"`
			Type  string `json:"type"`
		} `json:"notification"`
	})

	if err := c.BodyParser(body); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
	}

	if len(body.Token) == 0 {
		return fiber.NewError(fiber.StatusBadRequest, "No tokens provided")
	}


	var successCount, failureCount int
	for _, token := range body.Token {
		err := sendFCMMessage(token, body.Notification.Title, body.Notification.Body)

		if err != nil {
			log.Printf("❌ Failed to send to %s: %v", token, err)
			failureCount++
		} else {
			log.Printf("✅ Notification sent to %s", token)
			successCount++
		}
	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message": "Admin notifications sent",
		"success": successCount,
		"failure": failureCount,
	})
}

func (ctl *UtilsController) GetAppConfig(c *fiber.Ctx) error {
	cid, _ := strconv.Atoi(c.Query("configid"))

	result, err := ctl.utilsService.GetAppConfig(cid)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    500,
			"message": "Failed to fetch app config",
			"status":  false,
		})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": result,
	})
}

func (ctl *UtilsController) GetAppCategory(c *fiber.Ctx) error {
	result, err := ctl.utilsService.GetAppCategory()
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
			"code":    500,
			"message": "Failed to fetch app categories",
			"status":  false,
		})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{
		"code":    http.StatusOK,
		"message": "Success",
		"status":  true,
		"details": result,
	})
}



