package api

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"project-dashboard/api/utils"
	"project-dashboard/models"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// UploadFile handles file uploads
func UploadFile(c *gin.Context) {
	user, exists := c.Get("user")
	if !exists {
		utils.ErrorResponse(c, http.StatusUnauthorized, "User not authenticated")
		return
	}

	userObj := user.(*models.User)
	project, _ := c.Get("project")
	projectObj := project.(*models.Project)

	// Get task ID if provided
	taskIDStr := c.PostForm("task_id")
	var taskID *uint
	if taskIDStr != "" {
		if id, err := strconv.ParseUint(taskIDStr, 10, 32); err == nil {
			taskIDUint := uint(id)
			taskID = &taskIDUint
		}
	}

	// Get file from form
	file, header, err := c.Request.FormFile("file")
	if err != nil {
		utils.ErrorResponse(c, http.StatusBadRequest, "No file provided")
		return
	}
	defer file.Close()

	// Validate file size (10MB limit)
	const maxFileSize = 10 << 20 // 10MB
	if header.Size > maxFileSize {
		utils.ErrorResponse(c, http.StatusBadRequest, "File size too large (max 10MB)")
		return
	}

	// Generate unique filename
	ext := filepath.Ext(header.Filename)
	fileName := uuid.New().String() + ext

	// Create uploads directory if it doesn't exist
	uploadDir := "./uploads"
	if err := os.MkdirAll(uploadDir, 0755); err != nil {
		utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to create upload directory")
		return
	}

	// Save file
	filePath := filepath.Join(uploadDir, fileName)
	dst, err := os.Create(filePath)
	if err != nil {
		utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to create file")
		return
	}
	defer dst.Close()

	if _, err := io.Copy(dst, file); err != nil {
		utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to save file")
		return
	}

	// Get MIME type
	mimeType := header.Header.Get("Content-Type")
	if mimeType == "" {
		mimeType = "application/octet-stream"
	}

	// Create file record
	fileUpload := models.FileUpload{
		FileName:     fileName,
		OriginalName: header.Filename,
		FilePath:     filePath,
		FileSize:     header.Size,
		MimeType:     mimeType,
		UploadedByID: userObj.ID,
		ProjectID:    &projectObj.ID,
		TaskID:       taskID,
	}

	if err := models.GetDB().Create(&fileUpload).Error; err != nil {
		// Clean up file if database save fails
		os.Remove(filePath)
		utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to save file record")
		return
	}

	// Load file with relationships
	models.GetDB().
		Preload("UploadedBy").
		Preload("Project").
		Preload("Task").
		First(&fileUpload, fileUpload.ID)

	utils.SuccessResponse(c, http.StatusCreated, "File uploaded successfully", fileUpload)
}

// GetFiles returns all files for a project
func GetFiles(c *gin.Context) {
	project, exists := c.Get("project")
	if !exists {
		utils.ErrorResponse(c, http.StatusNotFound, "Project not found")
		return
	}

	projectObj := project.(*models.Project)

	var files []models.FileUpload

	if err := models.GetDB().
		Preload("UploadedBy").
		Preload("Project").
		Preload("Task").
		Where("project_id = ?", projectObj.ID).
		Order("created_at DESC").
		Find(&files).Error; err != nil {
		utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to fetch files")
		return
	}

	utils.SuccessResponse(c, http.StatusOK, "Files retrieved successfully", files)
}

// GetTaskFiles returns all files for a specific task
func GetTaskFiles(c *gin.Context) {
	project, _ := c.Get("project")
	projectObj := project.(*models.Project)

	taskID := c.Param("task_id")
	if taskID == "" {
		utils.ErrorResponse(c, http.StatusBadRequest, "Task ID required")
		return
	}

	// Verify task exists and belongs to project
	var task models.Task
	if err := models.GetDB().
		Where("id = ? AND project_id = ?", taskID, projectObj.ID).
		First(&task).Error; err != nil {
		utils.ErrorResponse(c, http.StatusNotFound, "Task not found")
		return
	}

	var files []models.FileUpload

	if err := models.GetDB().
		Preload("UploadedBy").
		Preload("Project").
		Preload("Task").
		Where("task_id = ?", taskID).
		Order("created_at DESC").
		Find(&files).Error; err != nil {
		utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to fetch task files")
		return
	}

	utils.SuccessResponse(c, http.StatusOK, "Task files retrieved successfully", files)
}

// DownloadFile serves a file for download
func DownloadFile(c *gin.Context) {
	project, _ := c.Get("project")
	projectObj := project.(*models.Project)

	fileID := c.Param("file_id")
	if fileID == "" {
		utils.ErrorResponse(c, http.StatusBadRequest, "File ID required")
		return
	}

	var fileUpload models.FileUpload
	if err := models.GetDB().
		Where("id = ? AND project_id = ?", fileID, projectObj.ID).
		First(&fileUpload).Error; err != nil {
		utils.ErrorResponse(c, http.StatusNotFound, "File not found")
		return
	}

	// Check if file exists on disk
	if _, err := os.Stat(fileUpload.FilePath); os.IsNotExist(err) {
		utils.ErrorResponse(c, http.StatusNotFound, "File not found on disk")
		return
	}

	// Set headers for file download
	c.Header("Content-Description", "File Transfer")
	c.Header("Content-Transfer-Encoding", "binary")
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileUpload.OriginalName))
	c.Header("Content-Type", "application/octet-stream")

	// Serve file
	c.File(fileUpload.FilePath)
}

// DeleteFile deletes a file
func DeleteFile(c *gin.Context) {
	project, _ := c.Get("project")
	projectObj := project.(*models.Project)

	fileID := c.Param("file_id")
	if fileID == "" {
		utils.ErrorResponse(c, http.StatusBadRequest, "File ID required")
		return
	}

	var fileUpload models.FileUpload
	if err := models.GetDB().
		Where("id = ? AND project_id = ?", fileID, projectObj.ID).
		First(&fileUpload).Error; err != nil {
		utils.ErrorResponse(c, http.StatusNotFound, "File not found")
		return
	}

	// Delete file from disk
	if err := os.Remove(fileUpload.FilePath); err != nil {
		// Log error but continue with database deletion
		fmt.Printf("Failed to delete file from disk: %v\n", err)
	}

	// Delete file record from database
	if err := models.GetDB().Delete(&fileUpload).Error; err != nil {
		utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to delete file record")
		return
	}

	utils.SuccessResponse(c, http.StatusOK, "File deleted successfully", nil)
}
