package com.example.demo.controller;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.example.demo.dto.ImageDto;
import com.example.demo.dto.RecipeDto;
import com.example.demo.dto.RecipeIngredientDto;
import com.example.demo.dto.StepDto;
import com.example.demo.dto.TagDto;
import com.example.demo.service.RecipeService;

@RestController
public class RecipeUploadController {

	private final RecipeService recipeService;

	@Value("${app.upload.dir}")
	private String uploadDir;

	public RecipeUploadController(RecipeService recipeService) {
		this.recipeService = recipeService;
	}

	@PostMapping(value = "/api/recipes/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public ResponseEntity<?> createRecipeWithUpload(@ModelAttribute RecipeUploadForm form, Principal principal) {
		try {
			RecipeDto dto = buildRecipeDto(form, true);
			String currentUsername = principal.getName();
			RecipeDto saved = recipeService.saveRecipe(dto, currentUsername);
			return new ResponseEntity<>(saved, HttpStatus.CREATED);

		} catch (IllegalArgumentException e) {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
			return ResponseEntity.status(HttpStatus.BAD_REQUEST)
					.body("Recipe creation failed: " + e.getMessage());
		}
	}

	@PostMapping(value = "/api/recipes/{id}/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public ResponseEntity<?> updateRecipeWithUpload(
			@PathVariable Integer id,
			@ModelAttribute RecipeUploadForm form,
			Principal principal) {
		try {
			RecipeDto dto = buildRecipeDto(form, false);
			String currentUsername = principal.getName();
			RecipeDto updated = recipeService.updateRecipe(dto, id, currentUsername);
			return new ResponseEntity<>(updated, HttpStatus.OK);

		} catch (IllegalArgumentException e) {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
			return ResponseEntity.status(HttpStatus.BAD_REQUEST)
					.body("Recipe update failed: " + e.getMessage());
		}
	}

	private RecipeDto buildRecipeDto(RecipeUploadForm form, boolean isCreate) throws IOException {
		RecipeDto dto = new RecipeDto();
		dto.setTitle(safeTrim(form.getTitle()));
		dto.setDescription(safeTrim(form.getDescription()));
		dto.setPrepTimeMinutes(parseInteger(form.getPrepTimeMinutes()));
		dto.setCookTimeMinutes(parseInteger(form.getCookTimeMinutes()));
		dto.setServings(parseInteger(form.getServings()));
		dto.setStatus("DRAFT");
		dto.setCost(parseInteger(form.getCost()));

		List<RecipeIngredientDto> ingredientDtos = new ArrayList<>();
		if (form.getIngredientName() != null) {
			for (int i = 0; i < form.getIngredientName().size(); i++) {
				String ingredientName = getListValue(form.getIngredientName(), i);
				if (ingredientName == null || ingredientName.isBlank()) {
					continue;
				}

				String quantityString = getListValue(form.getIngredientQuantity(), i);
				String quantity = null;
				if (quantityString != null && !quantityString.isBlank()) {
					quantity = quantityString.trim();
					validateQuantity(quantity);
				}

				String unit = getListValue(form.getIngredientUnit(), i);
				String notes = getListValue(form.getIngredientNotes(), i);

				RecipeIngredientDto riDto = new RecipeIngredientDto(
				        ingredientName.trim(),
				        quantity,
				        unit != null ? unit.trim() : "",
				        notes != null ? notes.trim() : "");
				riDto.setIndex(i);
				ingredientDtos.add(riDto);
			}
		}
		dto.setIngredients(ingredientDtos);

		List<StepDto> stepDtos = new ArrayList<>();
		if (form.getStepInstruction() != null) {
			for (int i = 0; i < form.getStepInstruction().size(); i++) {
				String instruction = form.getStepInstruction().get(i);
				if (instruction == null || instruction.isBlank()) {
					continue;
				}
				stepDtos.add(new StepDto(i + 1, instruction.trim()));
			}
		}
		dto.setSteps(stepDtos);

		List<TagDto> tagDtos = new ArrayList<>();
		if (form.getTags() != null) {
			for (String tag : form.getTags()) {
				if (tag != null && !tag.isBlank()) {
					tagDtos.add(new TagDto(tag.trim()));
				}
			}
		}
		dto.setTags(tagDtos);

		MultipartFile imageFile = form.getImage();
		boolean removeImage = Boolean.TRUE.equals(form.getRemoveImage());

		if (imageFile != null && !imageFile.isEmpty()) {
			List<ImageDto> imageDtos = new ArrayList<>();
			String imageUrl = saveUploadedFile(imageFile);
			imageDtos.add(new ImageDto(imageUrl));
			dto.setImages(imageDtos);
		} else if (removeImage) {
			dto.setImages(new ArrayList<>());
		} else if (isCreate) {
			dto.setImages(new ArrayList<>());
		} else {
			dto.setImages(null);
		}

		return dto;
	}

	private Integer parseInteger(String value) {
		if (value == null || value.isBlank()) {
			return null;
		}
		return Integer.valueOf(value.trim());
	}

	private String getListValue(List<String> list, int index) {
		if (list == null || index >= list.size()) {
			return null;
		}
		return list.get(index);
	}

	private String safeTrim(String value) {
		return value == null ? null : value.trim();
	}

	private void validateQuantity(String value) {
		String cleaned = value.trim();

		if (cleaned.matches("^\\d+(\\.\\d+)?$")) {
			return;
		}

		if (cleaned.matches("^\\d+/\\d+$")) {
			String[] parts = cleaned.split("/");
			BigDecimal numerator = new BigDecimal(parts[0]);
			BigDecimal denominator = new BigDecimal(parts[1]);

			if (denominator.compareTo(BigDecimal.ZERO) == 0) {
				throw new IllegalArgumentException("Ingredient quantity cannot have a zero denominator.");
			}
			if (numerator.compareTo(BigDecimal.ZERO) < 0 || denominator.compareTo(BigDecimal.ZERO) < 0) {
				throw new IllegalArgumentException("Ingredient quantity must be positive.");
			}
			return;
		}

		if (cleaned.matches("^\\d+\\s+\\d+/\\d+$")) {
			String[] mixed = cleaned.split("\\s+");
			BigDecimal whole = new BigDecimal(mixed[0]);

			String[] fraction = mixed[1].split("/");
			BigDecimal numerator = new BigDecimal(fraction[0]);
			BigDecimal denominator = new BigDecimal(fraction[1]);

			if (denominator.compareTo(BigDecimal.ZERO) == 0) {
				throw new IllegalArgumentException("Ingredient quantity cannot have a zero denominator.");
			}
			if (whole.compareTo(BigDecimal.ZERO) < 0
					|| numerator.compareTo(BigDecimal.ZERO) < 0
					|| denominator.compareTo(BigDecimal.ZERO) < 0) {
				throw new IllegalArgumentException("Ingredient quantity must be positive.");
			}
			return;
		}

		throw new IllegalArgumentException(
				"Invalid ingredient quantity: " + value + ". Use values like 1, 1.5, 1/4, or 2 1/2.");
	}

	private String saveUploadedFile(MultipartFile file) throws IOException {
		String originalFilename = StringUtils.cleanPath(file.getOriginalFilename());
		String extension = "";

		int dotIndex = originalFilename.lastIndexOf('.');
		if (dotIndex >= 0) {
			extension = originalFilename.substring(dotIndex);
		}

		String storedFilename = UUID.randomUUID() + extension;

		Path uploadPath = Paths.get(uploadDir).toAbsolutePath().normalize();
		if (!Files.exists(uploadPath)) {
			Files.createDirectories(uploadPath);
		}

		Path destination = uploadPath.resolve(storedFilename);
		Files.copy(file.getInputStream(), destination, StandardCopyOption.REPLACE_EXISTING);

		return "/uploads/" + storedFilename;
	}
}