<?php

namespace App\Jobs;

use App\Employee;
use App\Payout;
use App\PointTable;
use App\ReportJob;
use App\Services\EmployeeService;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Bus\Batchable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;

class GenerateCsvReportOne implements ShouldQueue
{
	use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
	
	protected $reportJob;
	protected $filters;
	protected $currentEmployeeId;
	
	/**
	 * The number of seconds the job can run before timing out.
	 *
	 * @var int
	 */
	public $timeout = 3600;
	
	/**
	 * Indicate if the job should be marked as failed on timeout.
	 *
	 * @var bool
	 */
	public $failOnTimeout = false;
	
	/**
	 * Create a new job instance.
	 *
	 * @param ReportJob $reportJob
	 * @param $filters
	 */
	public function __construct(ReportJob $reportJob, $filters)
	{
		$this->onQueue(config('queue.connections.redis.queue'));
		$this->reportJob = $reportJob;
		$this->filters = $filters;
	}
	
	/**
	 * @return bool
	 */
	public function isFailOnTimeout(): bool
	{
		return $this->failOnTimeout;
	}
	
	/**
	 * Execute the job.
	 *
	 * @return void
	 */
	public function handle()
	{
		try {
			ini_set('max_execution_time', 300);
			ini_set('memory_limit', '512M');
			
			$this->reportJob->update([
				'status'     => 'processing',
				'updated_at' => Carbon::now(),
			]);
			
			$employeeService = new EmployeeService();
			$savePath = public_path('report/' . $this->reportJob->code . '.csv');
			$payouts = Payout::select('years', 'rate')->orderBy('years')->get()->toArray();
			$pointTable = PointTable::select('years', 'points')->pluck('points', 'years')->toArray();
			$employees = Employee::with('department', 'transitionPoint', 'levelHistories', 'positionHistories')->select('*')->whereNotNull('join_date');
			
			/*if (isset($this->filters['years']) && $this->filters['years'] !== null && $this->filters['years'] !== "") {
				$startFiscalYear = Carbon::parse($this->filters['years'] . "-04-01");
				$endFiscalYear = Carbon::parse($this->filters['years'] . "-03-31")->addYear(1);
				$currentDate = Carbon::now();
				
				if ($currentDate->greaterThan($endFiscalYear)) {
					// Đã kết thúc năm tài chính.
					$employees->where(function ($query) use ($startFiscalYear, $endFiscalYear) {
						$query->where('employment_status', Employee::STATUS_RESIGNED)
							->where('resigned_date', '>=', $startFiscalYear->format('Y-m-d'))
							->where('resigned_date', '<=', $endFiscalYear->format('Y-m-d'));
					});
				} else {
					// Chưa kết thúc năm tài chính.
					$employees->where(function ($query) use ($startFiscalYear, $endFiscalYear) {
						$query->where('employment_status', Employee::STATUS_WORKING)
							->orWhere(function ($subQuery) use ($startFiscalYear, $endFiscalYear) {
								$subQuery->where('employment_status', Employee::STATUS_RESIGNED)
									->where('resigned_date', '>=', $startFiscalYear->format('Y-m-d'))
									->where('resigned_date', '<=', $endFiscalYear->format('Y-m-d'));
							});
					});
				}
			}*/
			
			if (isset($this->filters['employee_name']) && $this->filters['employee_name'] != null && $this->filters['employee_name'] != "") {
				$employees->where('full_name', 'like', '%' . $this->filters['employee_name'] . '%');
			}
			
			if (isset($this->filters['status']) && in_array($this->filters['status'], [1, 2, 3, 4])) {
				if ($this->filters['status'] == 2) {
					$employees->resigned();
				} elseif ($this->filters['status'] == 3) {
					$employees->leave();
				} elseif ($this->filters['status'] == 4) {
					$currentYear = Carbon::now()->year;
					$retirementAge = (integer)config('settings.retirement_age');
					$retirementAge = (!$retirementAge || $retirementAge < 10 || $retirementAge > 100) ? 60 : $retirementAge;
					$retirementBirthYear = $currentYear - ($retirementAge - 1);
					$startDate = Carbon::createFromDate($retirementBirthYear, 1, 1)->startOfDay();
					$endDate = Carbon::createFromDate($retirementBirthYear, 12, 31)->endOfDay();
					$employees->working()
						->whereNotNull('date_of_birth')
						->whereBetween('date_of_birth', [$startDate->format('Y-m-d'), $endDate->format('Y-m-d')]);
				} else {
					$employees->working();
				}
			}
			
			$employees = $employees->orderBy('id')->get();
			
			$totalPointYear = 0;
			$totalPointLevel = 0;
			$totalPointPosition = 0;
			$totalAmountYear = 0;
			$totalAmountLevel = 0;
			$totalAmountPosition = 0;
			$totalPoint = 0;
			$totalAmount = 0;
			$totalAmountSeverancePayment = 0;
			
			// Mở file để ghi
			$file = fopen($savePath, 'w');
			
			// Ghi header UTF-8 BOM để Excel hiển thị tiếng Nhật đúng
			fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));
			
			// Ghi header
			fputcsv($file, [
				'所属コード',      // Department code
				'所属名',         // Department name
				'個人コード',       // Employee code
				'氏名',          // Full name
				'生年月日',       // Date of birth
				'入社年月日',         // Join date
				'退職年月日',         // Resigned date
				'勤続年数',       // Tenure year
				'勤続ポイント',    // Tenure point
				'勤続支給額',       // Tenure amount
				'資格ポイント',    // Level point
				'資格支給額',       // Level amount
				'役職ポイント',    // Position point
				'役職支給額',       // Position amount
				'合計ポイント',    // Total point
				'（修正前）支給額（a）',       // Total amount
				'支給率（ｂ）',         // Payment percent
				'総支給額（a）×（ｂ）'        // Severance amount
			]);
			
			foreach ($employees as $employee) {
				$this->currentEmployeeId = $employee->id;
				$endFiscalYear = null;
				$calculateFiscalYear = Carbon::now()->year;
				
				if (isset($this->filters['years']) && $this->filters['years'] !== null && $this->filters['years'] !== "") {
					$endFiscalYear = (int) $this->filters['years'];
				}
				
				if ($employee->employment_status == Employee::STATUS_RESIGNED && $employee->resigned_date != null && $employee->resigned_date != "") {
					$resignedDate = Carbon::parse($employee->resigned_date);
					$fiscalResignedYear = Carbon::create($resignedDate->year, 4, 1);
					if ($resignedDate->greaterThan($fiscalResignedYear)) {
						$calculateFiscalYear = $resignedDate->addYear(1)->year;
					} else {
						$calculateFiscalYear = $resignedDate->year;
					}
					
					if ($endFiscalYear != null && $endFiscalYear < $calculateFiscalYear) {
						$calculateFiscalYear = $endFiscalYear;
					}
				} else {
					if ($endFiscalYear != null) $calculateFiscalYear = $endFiscalYear;
				}
				
				$result = $employeeService->calculateSeverancePayment($employee, $payouts, $pointTable, $calculateFiscalYear);
				$paymentPercent = $result['payment_percent'];
				if (is_numeric($paymentPercent)) {
					$formatted = number_format($paymentPercent, 2, '.', '');
					$paymentPercent = rtrim(rtrim($formatted, '0'), '.');
				}
				
				fputcsv($file, [
					$employee->department->code,
					$employee->department->name,
					$employee->employee_code,
					$employee->full_name,
					$result['date_of_birth'],
					$result['join_date'],
					$result['resigned_date'],
					number_format($result['tenure_year'], 2),
					number_format($result['tenure_point'], 2),
					number_format($result['tenure_amount'], 2),
					number_format($result['level_point'], 2),
					number_format($result['level_amount'], 2),
					number_format($result['position_point'], 2),
					number_format($result['position_amount'], 2),
					number_format($result['total_point'], 2),
					number_format($result['total_amount'], 2),
					$paymentPercent . '%',
					number_format($result['final_amount'], 2)
				], ',', '"', '\\');
				
				$totalPointYear += $result['tenure_point'];
				$totalPointLevel += $result['level_point'];
				$totalPointPosition += $result['position_point'];
				
				$totalAmountYear += $result['tenure_amount'];
				$totalAmountLevel += $result['level_amount'];
				$totalAmountPosition += $result['position_amount'];
				
				$totalPoint += $result['total_point'];
				$totalAmount += $result['total_amount'];
				$totalAmountSeverancePayment += $result['final_amount'];
			}
			
			// Total line
			fputcsv($file, [
				'合計',
				'',
				'',
				'',
				'',
				'',
				'',
				'',
				number_format($totalPointYear, 2),
				number_format($totalAmountYear, 2),
				number_format($totalPointLevel, 2),
				number_format($totalAmountLevel, 2),
				number_format($totalPointPosition, 2),
				number_format($totalAmountPosition, 2),
				number_format($totalPoint, 2),
				number_format($totalAmount, 2),
				'',
				number_format($totalAmountSeverancePayment, 2)
			], ',', '"', '\\');
			
			// Close file
			fclose($file);
			
			// Set Status
			$this->reportJob->update([
				'file'        => $savePath,
				'total_sheet' => $employees->count(),
				'status'      => 'success',
				'updated_at'  => Carbon::now(),
			]);
		} catch (\Exception $exception) {
			$this->reportJob->update([
				'status'     => 'failed',
				'updated_at' => Carbon::now(),
			]);
			Log::error("[GenerateCsvReportOne] Generate excel report one error [ID: {$this->currentEmployeeId}]: " . $exception->getMessage());
		}
	}
}
