// Package hr implements employees, attendance, leaves, payrolls and
// performances (business-rules.md §8, domain-map.md D9). Self-service routes
// resolve the authenticated user to their employee row via employees.user_id.
package hr

import (
	"context"
	"time"

	"github.com/shopspring/decimal"

	"github.com/TaruLab/keluargamart-backend/internal/db"
)

// Employee mirrors employees (EMP-<seq> codes, soft delete).
type Employee struct {
	ID              int64           `json:"id"`
	EmployeeNumber  string          `json:"employee_number"`
	UserID          *int64          `json:"user_id"`
	Name            string          `json:"name"`
	NIK             string          `json:"nik"`
	Department      string          `json:"department"`
	JobLevel        string          `json:"job_level"`
	Position        string          `json:"position"`
	EmploymentType  string          `json:"employment_type"`
	JoinDate        *string         `json:"join_date"`
	EndDate         *string         `json:"end_date"`
	BasicSalary          decimal.Decimal `json:"basic_salary"`
	Allowance            decimal.Decimal `json:"allowance"`
	Phone                string          `json:"phone"`
	Email                string          `json:"email"`
	Address              string          `json:"address"`
	City                 string          `json:"city"`
	BankName             string          `json:"bank_name"`
	BankAccount          string          `json:"bank_account"`
	BankAccountName      string          `json:"bank_account_name"`
	NPWP                 string          `json:"npwp"`
	BpjsKetenagakerjaanNo string         `json:"bpjs_ketenagakerjaan_no"`
	BpjsKesehatanNo      string          `json:"bpjs_kesehatan_no"`
	Photo                string          `json:"photo"`
	IsActive             bool            `json:"is_active"`
	LeaveBalance         decimal.Decimal `json:"leave_balance"`
	LeaveUsed            decimal.Decimal `json:"leave_used"`
	DeletedAt            *time.Time      `json:"deleted_at"`
}

// EmployeeInput mirrors the openapi EmployeeInput.
type EmployeeInput struct {
	EmployeeNumber       string          `json:"employee_number"`
	UserID               *int64          `json:"user_id"`
	Name                 string          `json:"name"`
	NIK                  string          `json:"nik"`
	Department           string          `json:"department"`
	JobLevel             string          `json:"job_level"`
	Position             string          `json:"position"`
	EmploymentType       string          `json:"employment_type"`
	JoinDate             *string         `json:"join_date"`
	EndDate              *string         `json:"end_date"`
	BasicSalary          decimal.Decimal `json:"basic_salary"`
	Allowance            decimal.Decimal `json:"allowance"`
	Phone                string          `json:"phone"`
	Email                string          `json:"email"`
	Address              string          `json:"address"`
	City                 string          `json:"city"`
	BankName             string          `json:"bank_name"`
	BankAccount          string          `json:"bank_account"`
	BankAccountName      string          `json:"bank_account_name"`
	NPWP                 string          `json:"npwp"`
	BpjsKetenagakerjaanNo string         `json:"bpjs_ketenagakerjaan_no"`
	BpjsKesehatanNo      string          `json:"bpjs_kesehatan_no"`
	IsActive             bool            `json:"is_active"`
}

// Attendance mirrors attendances (one record per employee per day).
type Attendance struct {
	ID             int64            `json:"id"`
	EmployeeID     int64            `json:"employee_id"`
	AttendanceDate string           `json:"attendance_date"`
	ClockIn        *time.Time       `json:"clock_in"`
	ClockOut       *time.Time       `json:"clock_out"`
	ClockInLat     *float64         `json:"clock_in_lat"`
	ClockInLng     *float64         `json:"clock_in_lng"`
	ClockOutLat    *float64         `json:"clock_out_lat"`
	ClockOutLng    *float64         `json:"clock_out_lng"`
	Status         string           `json:"status"`
	WorkHours      *decimal.Decimal `json:"work_hours"`
	OvertimeHours  *decimal.Decimal `json:"overtime_hours"`
	Notes          string           `json:"notes"`
	EmployeeName   string           `json:"employee_name,omitempty"`
	Department     string           `json:"department,omitempty"`
}

// ClockInOutInput mirrors the openapi ClockInOutInput.
type ClockInOutInput struct {
	Lat   *float64 `json:"lat"`
	Lng   *float64 `json:"lng"`
	Notes string   `json:"notes"`
	Photo string   `json:"photo"`
}

// AttendanceInput mirrors the openapi AttendanceInput (admin backfill).
type AttendanceInput struct {
	EmployeeID     int64      `json:"employee_id"`
	AttendanceDate string     `json:"attendance_date"`
	ClockIn        *time.Time `json:"clock_in"`
	ClockOut       *time.Time `json:"clock_out"`
	Status         string     `json:"status"`
	Notes          string     `json:"notes"`
}

// Leave mirrors leaves (pending -> approved/rejected/cancelled).
type Leave struct {
	ID         int64      `json:"id"`
	EmployeeID int64      `json:"employee_id"`
	LeaveType  string     `json:"leave_type"`
	StartDate  string     `json:"start_date"`
	EndDate    string     `json:"end_date"`
	TotalDays  int        `json:"total_days"`
	Reason     string     `json:"reason"`
	Status     string     `json:"status"`
	ApprovedBy *int64     `json:"approved_by"`
	ApprovedAt *time.Time `json:"approved_at"`
	Notes      string     `json:"notes"`
	// EmployeeName is joined on the leaves list only.
	EmployeeName string `json:"employee_name,omitempty"`
}

// LeaveInput mirrors the openapi LeaveInput.
type LeaveInput struct {
	// EmployeeID is the employee the leave belongs to. Zero (or the caller's
	// own id) files for oneself, which is what every non-HR caller can do.
	// Filing for anyone else requires hr.leaves.store.any — see
	// leave_target.go. This field did not exist before: the payload carried it
	// and the server silently ignored it, so a pick from the ERP's Karyawan
	// dropdown was filed against the caller instead.
	EmployeeID int64  `json:"employee_id"`
	LeaveType  string `json:"leave_type"`
	StartDate  string `json:"start_date"`
	EndDate    string `json:"end_date"`
	Reason     string `json:"reason"`
	Notes      string `json:"notes"`
}

// Payroll mirrors payrolls (unique per employee+period).
type Payroll struct {
	ID                  int64           `json:"id"`
	PayrollNumber       string          `json:"payroll_number"`
	EmployeeID          int64           `json:"employee_id"`
	Employee            *Employee       `json:"employee,omitempty"`
	PeriodMonth         int             `json:"period_month"`
	PeriodYear          int             `json:"period_year"`
	BasicSalary         decimal.Decimal `json:"basic_salary"`
	Allowances          decimal.Decimal `json:"allowances"`
	OvertimePay         decimal.Decimal `json:"overtime_pay"`
	Bonus               decimal.Decimal `json:"bonus"`
	Deductions          decimal.Decimal `json:"deductions"`
	BpjsKetenagakerjaan decimal.Decimal `json:"bpjs_ketenagakerjaan"`
	BpjsKesehatan       decimal.Decimal `json:"bpjs_kesehatan"`
	Pph21               decimal.Decimal `json:"pph21"`
	NetSalary           decimal.Decimal `json:"net_salary"`
	PaymentDate         *string         `json:"payment_date"`
	Status              string          `json:"status"`
	Notes               string          `json:"notes"`
	// GrossSalary = basic_salary + allowances + overtime_pay + bonus.
	GrossSalary decimal.Decimal `json:"gross_salary"`
	// EmployeeName is joined on the payrolls list only.
	EmployeeName string `json:"employee_name,omitempty"`
}

// Performance mirrors performances (monthly scores + derived total/grade).
type Performance struct {
	ID               int64           `json:"id"`
	EmployeeID       int64           `json:"employee_id"`
	EvaluatorID      *int64          `json:"evaluator_id"`
	PeriodMonth      int             `json:"period_month"`
	PeriodYear       int             `json:"period_year"`
	KPIScore         decimal.Decimal `json:"kpi_score"`
	AttitudeScore    decimal.Decimal `json:"attitude_score"`
	TeamworkScore    decimal.Decimal `json:"teamwork_score"`
	AchievementScore decimal.Decimal `json:"achievement_score"`
	TotalScore       decimal.Decimal `json:"total_score"`
	Grade            string          `json:"grade"`
	Achievements     string          `json:"achievements"`
	Improvements     string          `json:"improvements"`
	Notes            string          `json:"notes"`
	EmployeeName     string          `json:"employee_name,omitempty"`
	EvaluatorName    string          `json:"evaluator_name,omitempty"`
}

// PerformanceInput mirrors the openapi PerformanceInput.
type PerformanceInput struct {
	EmployeeID       int64           `json:"employee_id"`
	PeriodMonth      int             `json:"period_month"`
	PeriodYear       int             `json:"period_year"`
	KPIScore         decimal.Decimal `json:"kpi_score"`
	AttitudeScore    decimal.Decimal `json:"attitude_score"`
	TeamworkScore    decimal.Decimal `json:"teamwork_score"`
	AchievementScore decimal.Decimal `json:"achievement_score"`
	Achievements     string          `json:"achievements"`
	Improvements     string          `json:"improvements"`
	Notes            string          `json:"notes"`
}

// Filter carries shared list parameters (page, per_page, status, date range).
type Filter struct {
	Page, PerPage int
	Status        string
	DateFrom      string
	DateTo        string
	// ERP column sort (sort_by/sort_dir), whitelist-gated in SQL. Applies to
	// ListEmployees; other lists ignore it.
	SortBy  string
	SortDir string
}

// PayPayrollInput carries parameters for paying/disbursing a payroll.
type PayPayrollInput struct {
	PaymentDate   string `json:"payment_date"`
	PaymentMethod string `json:"payment_method"` // cash or bank
	AccountCode   string `json:"account_code"`   // e.g. 1000.01.01 (Kas) or 1000.03.01 (Bank BCA)
	Notes         string `json:"notes"`
}

// AttendanceSummary aggregates an employee's monthly attendance figures.
type AttendanceSummary struct {
	PresentDays        int             `json:"present_days"`
	LateDays           int             `json:"late_days"`
	LeaveDays          int             `json:"leave_days"`
	AbsentDays         int             `json:"absent_days"`
	UnpaidLeaveDays    int             `json:"unpaid_leave_days"`
	TotalOvertimeHours decimal.Decimal `json:"total_overtime_hours"`
}

// Store is the HR persistence seam. Persistence uses the sqlc-generated types;
// the service converts rows into the API views above.
type Store interface {
	ListEmployees(ctx context.Context, f Filter, search string, active *bool) ([]db.ListEmployeesRow, int64, error)
	EmployeeByID(ctx context.Context, id int64) (db.GetEmployeeByIDRow, error)
	EmployeeByUserID(ctx context.Context, userID int64) (db.GetEmployeeByUserIDRow, error)
	CreateEmployee(ctx context.Context, p db.CreateEmployeeParams) (db.CreateEmployeeRow, error)
	UpdateEmployee(ctx context.Context, p db.UpdateEmployeeParams) error
	DeleteEmployee(ctx context.Context, id int64) error
	GetLatestEmployeeNumber(ctx context.Context) (string, error)

	ListAttendance(ctx context.Context, f Filter, employeeID *int64) ([]db.ListAttendanceRow, int64, error)
	AttendanceByID(ctx context.Context, id int64) (db.GetAttendanceByIDRow, error)
	AttendanceForDay(ctx context.Context, employeeID int64, date string) (db.GetAttendanceForDayRow, error)
	CreateAttendance(ctx context.Context, p db.CreateAttendanceParams) (db.CreateAttendanceRow, error)
	UpdateAttendance(ctx context.Context, p db.UpdateAttendanceParams) error
	UpsertLeaveAttendance(ctx context.Context, p db.UpsertLeaveAttendanceParams) error
	ListAttendanceForEmployeePeriod(ctx context.Context, p db.ListAttendanceForEmployeePeriodParams) ([]db.ListAttendanceForEmployeePeriodRow, error)
	ListApprovedLeavesForEmployeePeriod(ctx context.Context, p db.ListApprovedLeavesForEmployeePeriodParams) ([]db.ListApprovedLeavesForEmployeePeriodRow, error)

	ListLeaves(ctx context.Context, f Filter, employeeID *int64) ([]db.ListLeavesRow, int64, error)
	LeaveByID(ctx context.Context, id int64) (db.GetLeaveByIDRow, error)
	CreateLeave(ctx context.Context, p db.CreateLeaveParams) (db.CreateLeaveRow, error)
	UpdateLeave(ctx context.Context, p db.UpdateLeaveParams) error
	DeductEmployeeLeave(ctx context.Context, p db.DeductEmployeeLeaveParams) (decimal.Decimal, error)

	ListPayrolls(ctx context.Context, f Filter, employeeID *int64) ([]db.ListPayrollsRow, int64, error)
	PayrollByID(ctx context.Context, id int64) (db.GetPayrollByIDRow, error)
	CreatePayroll(ctx context.Context, p db.CreatePayrollParams) (db.CreatePayrollRow, error)
	UpsertPayroll(ctx context.Context, p db.UpsertPayrollParams) (db.UpsertPayrollRow, error)
	UpdatePayroll(ctx context.Context, p db.UpdatePayrollParams) error
	PayPayroll(ctx context.Context, p db.PayPayrollParams) error
	ActiveEmployees(ctx context.Context) ([]db.ListActiveEmployeesRow, error)

	ListPerformances(ctx context.Context, f Filter) ([]db.ListPerformancesRow, int64, error)
	PerformanceByID(ctx context.Context, id int64) (db.GetPerformanceByIDRow, error)
	CreatePerformance(ctx context.Context, p db.CreatePerformanceParams) (db.CreatePerformanceRow, error)
	UpdatePerformance(ctx context.Context, p db.UpdatePerformanceParams) error
	DeletePerformance(ctx context.Context, id int64) error
}
