package com.example.todolist.service;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.multipart.MultipartFile;

import com.example.todolist.common.Utils;
import com.example.todolist.entity.AttachedFile;
import com.example.todolist.entity.Todolist;
import com.example.todolist.form.TaskData;
import com.example.todolist.form.TodoData;
import com.example.todolist.form.TodoQuery;
import com.example.todolist.repository.AttachedFileRepository;
import com.example.todolist.repository.TodolistRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class TodoService {
	private final MessageSource messageSource;
	private final AttachedFileRepository attachedFileRepository;
	private final TodolistRepository todolistRepository; // 追加

	@Value("${attached.file.path}")
	private String ATTACHED_FILE_PATH;

	// --------------------------------------------------------------------------------
	// Todo + Taskのチェック
	// --------------------------------------------------------------------------------
	public boolean isValid(TodoData todoData, BindingResult result, boolean isCreate, Locale locale) {
		boolean ans = true;

		// ----------------------------------------------------------------------------
		// Todo部分のチェック
		// ----------------------------------------------------------------------------
		// 件名が全角スペースだけで構成されていたらエラー
		if (!Utils.isBlank(todoData.getTitle())) {
			if (Utils.isAllDoubleSpace(todoData.getTitle())) {
				FieldError fieldError = new FieldError(result.getObjectName(), "title",
						messageSource.getMessage("DoubleSpace.todoData.title", null, locale));
				result.addError(fieldError);
				ans = false;
			}
		}

		// 期限が""ならチェックしない
		String deadline = todoData.getDeadline();
		if (!deadline.equals("")) {
			// yyyy-mm-dd形式チェック
			if (!Utils.isValidDateFormat(deadline)) {
				FieldError fieldError = new FieldError(result.getObjectName(), "deadline",
						messageSource.getMessage("InvalidFormat.todoData.deadline", null, locale));
				result.addError(fieldError);
				ans = false;

			} else {
				// 過去日付チェックは新規登録の場合のみ
				if (isCreate) {
					// 過去日付ならエラー
					if (!Utils.isTodayOrFurtureDate(deadline)) {
						FieldError fieldError = new FieldError(result.getObjectName(), "deadline",
								messageSource.getMessage("Past.todoData.deadline", null, locale));
						result.addError(fieldError);
						ans = false;
					}
				}
			}
		}

		// ----------------------------------------------------------------------------
		// Taskのチェック
		// ----------------------------------------------------------------------------
		List<TaskData> taskList = todoData.getTaskList();
		if (taskList != null) {
			// すべてのタスクに対して以下を実行する
			// 「タスクのn番目」という情報が必要なので(拡張for文でなく)for文を使用する
			for (int n = 0; n < taskList.size(); n++) {
				TaskData taskData = taskList.get(n);

				// タスクの件名が全角スペースだけで構成されていたらエラー
				if (!Utils.isBlank(taskData.getTitle())) {
					if (Utils.isAllDoubleSpace(taskData.getTitle())) {
						FieldError fieldError = new FieldError(result.getObjectName(), "taskList[" + n + "].title",
								messageSource.getMessage("DoubleSpace.todoData.title", null, locale));
						result.addError(fieldError);
						ans = false;
					}
				}

				// タスク期限のyyyy-mm-dd形式チェック
				String taskDeadline = taskData.getDeadline();
				if (!taskDeadline.equals("") && !Utils.isValidDateFormat(taskDeadline)) {
					FieldError fieldError = new FieldError(result.getObjectName(), "taskList[" + n + "].deadline",
							messageSource.getMessage("InvalidFormat.todoData.deadline", null, locale));
					result.addError(fieldError);
					ans = false;
				}
			}
		}

		return ans;
	}

	// --------------------------------------------------------------------------------
	// 検索条件のチェック
	// --------------------------------------------------------------------------------
	public boolean isValid(TodoQuery todoQuery, BindingResult result, Locale locale) {
		boolean ans = true;

		// 期限:開始の形式をチェック
		String date = todoQuery.getDeadlineFrom();
		if (!date.equals("") && !Utils.isValidDateFormat(date)) {
			FieldError fieldError = new FieldError(result.getObjectName(), "deadlineFrom",
					messageSource.getMessage("InvalidFormat.todoQuery.deadlineFrom", null, locale));
			result.addError(fieldError);
			ans = false;
		}

		// 期限:終了の形式をチェック
		date = todoQuery.getDeadlineTo();
		if (!date.equals("") && !Utils.isValidDateFormat(date)) {
			FieldError fieldError = new FieldError(result.getObjectName(), "deadlineTo",
					messageSource.getMessage("InvalidFormat.todoQuery.deadlineTo", null, locale));
			result.addError(fieldError);
			ans = false;
		}
		return ans;
	}

	// --------------------------------------------------------------------------------
	// 新規Taskのチェック
	// --------------------------------------------------------------------------------
	public boolean isValid(TaskData taskData, BindingResult result, Locale locale) {
		boolean ans = true;

		// タスクの件名が半角スペースだけ or "" ならエラー
		if (Utils.isBlank(taskData.getTitle())) {
			FieldError fieldError = new FieldError(result.getObjectName(), "newTask.title",
					messageSource.getMessage("NotBlank.taskData.title", null, locale));
			result.addError(fieldError);
			ans = false;

		} else {
			// タスクの件名が全角スペースだけで構成されていたらエラー
			if (Utils.isAllDoubleSpace(taskData.getTitle())) {
				FieldError fieldError = new FieldError(result.getObjectName(), "newTask.title",
						messageSource.getMessage("DoubleSpace.taskData.title", null, locale));
				result.addError(fieldError);
				ans = false;
			}
		}

		// 期限が""ならチェックしない
		String deadline = taskData.getDeadline();
		if (deadline.equals("")) {
			return ans;
		}

		// 期限のyyyy-mm-dd形式チェック
		if (!Utils.isValidDateFormat(deadline)) {
			FieldError fieldError = new FieldError(result.getObjectName(), "newTask.deadline",
					messageSource.getMessage("InvalidFormat.todoData.deadline", null, locale));
			result.addError(fieldError);
			ans = false;

		} else {
			// 過去日付ならエラー
			if (!Utils.isTodayOrFurtureDate(deadline)) {
				FieldError fieldError = new FieldError(result.getObjectName(), "newTask.deadline",
						messageSource.getMessage("Past.todoData.deadline", null, locale));
				result.addError(fieldError);
				ans = false;
			}
		}

		return ans;
	}

	// --------------------------------------------------------------------------------
	// 添付ファイル格納処理
	// --------------------------------------------------------------------------------
	public void saveAttachedFile(int todoId, String note, MultipartFile fileContents) {
		// アップロード元ファイル名
		String fileName = fileContents.getOriginalFilename();

		// 格納フォルダの存在チェック
		File uploadDir = new File(ATTACHED_FILE_PATH);
		if (!uploadDir.exists()) {
			// ないのでディレクトリ作成
			uploadDir.mkdirs();
		}

		// 添付ファイルの格納(upload)時刻を取得
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		String createTime = sdf.format(new Date());

		// テーブルへ格納するインスタンス作成
		AttachedFile af = new AttachedFile();
		af.setTodoId(todoId);
		af.setFileName(fileName);
		af.setCreateTime(createTime);
		af.setNote(note);

		// アップロードファイルの内容を取得
		byte[] contents;
		try (BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream(Utils.makeAttahcedFilePath(ATTACHED_FILE_PATH, af)))) {
			// アップロードファイルを書き込む
			contents = fileContents.getBytes();
			bos.write(contents);

			// テーブルへ記録
			attachedFileRepository.saveAndFlush(af);

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// --------------------------------------------------------------------------------
	// 添付ファイル削除処理(AttachedFile.idで削除)
	// --------------------------------------------------------------------------------
	public void deleteAttachedFile(int afId) {
		AttachedFile af = attachedFileRepository.findById(afId).get();
		File file = new File(Utils.makeAttahcedFilePath(ATTACHED_FILE_PATH, af));
		file.delete();
	}

	// --------------------------------------------------------------------------------
	// 添付ファイル削除処理(Todo.idで削除)
	// --------------------------------------------------------------------------------
	public void deleteAttachedFiles(Integer todoId) {
		File file;
		List<AttachedFile> attachedFiles = attachedFileRepository.findByTodoIdOrderById(todoId);
		for (AttachedFile af : attachedFiles) {
			file = new File(Utils.makeAttahcedFilePath(ATTACHED_FILE_PATH, af));
			file.delete();
		}
	}

	// --------------------------------------------------------------------------------
	// Todolist(v_todolist)の検索処理
	// --------------------------------------------------------------------------------
	public Page<Todolist> findByCriteria(TodoQuery todoQuery, Integer accountId, Pageable pageable) {
		// 件名
		String title = "";
		if (todoQuery.getTitle().length() > 0) {
			title = "%" + todoQuery.getTitle() + "%";
		} else {
			title = "%";
		}
		// 重要度
		List<Integer> importance = new ArrayList<>();
		if (todoQuery.getImportance() == -1) {
			// 指定なし

			importance.add(0);
			importance.add(1);
		} else {
			importance.add(todoQuery.getImportance());
		}
		// 緊急度
		List<Integer> urgency = new ArrayList<>();
		if (todoQuery.getUrgency() == -1) {
			// 指定なし
			urgency.add(0);
			urgency.add(1);
		} else {
			urgency.add(todoQuery.getUrgency());
		}
		// 期限：開始～
		java.sql.Date from;
		if (todoQuery.getDeadlineFrom().equals("")) {
			// 指定なし
			from = Utils.str2date("1900-01-01"); // String → java.sql.Date 変換メソッド
		} else {
			from = Utils.str2date(todoQuery.getDeadlineFrom());
		}
		// ～期限：終了で検索
		java.sql.Date to;
		if (todoQuery.getDeadlineTo().equals("")) {
			// 指定なし
			to = Utils.str2date("2999-12-31");
		} else {
			to = Utils.str2date(todoQuery.getDeadlineTo());
		}
		// 完了
		List<String> done = new ArrayList<>();
		if (todoQuery.getDone() != null && todoQuery.getDone().equals("Y")) {
			done.add("Y");
		} else {
			// 指定なし
			done.add("Y");
			done.add("N");
		}
		// 検索して結果を返す
		if (todoQuery.getDeadlineFrom().equals("") && todoQuery.getDeadlineTo().equals("")) {
			return todolistRepository.findByOwnerIdAndTitleLikeAndImportanceInAndUrgencyInAndDoneIn(accountId, title,
					importance, urgency, done, pageable);
		} else {
			return todolistRepository.findByOwnerIdAndTitleLikeAndImportanceInAndUrgencyInAndDeadlineBetweenAndDoneIn(
					accountId, title, importance, urgency, from, to, done, pageable);
		}
	}
}