#ifndef __PF_CONTROLLER_H__
#define __PF_CONTROLLER_H__

#include <time.h>
#include <math.h>
#include "jsonct.h"
#include "define.h"
#include "microgridtopology.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    double grid_p;
    double grid_q;
    double grid_s;
    double pf_real;
    double q_exp_total;
} pf_calc_result_t;

#define PF_OK                   0    /* 成功 */
#define PF_ERR_PARAM           -1    /* 参数非法（NULL / 越界） */
#define PF_ERR_METER_OFFLINE   -2    /* 关口表离线或无关口表数据 */
#define PF_ERR_TARGET_ILLEGAL  -3    /* 目标功率因数 F 非法 */
#define PF_ERR_DEVIATION_ILLEGAL -4  /* 回差 D 非法 */
#define PF_ERR_NAN_INF         -5    /* 计算出现 NaN/Inf */
#define PF_ERR_RATED_POWER     -6    /* 额定功率 ≤ 0 */
#define PF_ERR_C2D_YIELD       -7    /* 调节被 C2D 仲裁让位（有功优先） */
#define PF_ERR_NO_ADJUST       -8    /* 本周期不需要调节（死区/节流/未使能） */

typedef enum {
    PF_ENTRY_DC = 0,
    PF_ENTRY_AC = 1,
    PF_ENTRY_STAND = 2,
    PF_ENTRY_MAX = 3
} pf_entry_tag_t;

int pf_controller(double *q_target_total, time_t *last_adjust,
                  int major_state, int control_mode, pf_entry_tag_t entry_tag, double pcs_q_cur);

/* ========== 柜内超容保护 ========== */
/*
 * pf_cap_guard: 柜级视在钳位 Q_result = MIN(Q_alloc, sqrt(R²-P²))，优先保有功
 *   q_alloc: 分配来的目标无功
 *   rated_power: 柜额定功率（视在 kVA）
 *   act_power: 柜当前有功功率 kW
 *   q_result: 输出钳位后无功
 *   was_clamped: 输出是否被钳位（用于打日志，传NULL不关心）
 *   返回: 0 成功, PF_ERR_RATED_POWER 额定功率非法
 */
int pf_cap_guard(double q_alloc, double rated_power, double act_power,
                 double *q_result, int *was_clamped);

/* ========== 关口级视在保护 ========== */
/*
 * pf_grid_cap_guard: 关口级校验，按 EnReactivePriority 优先级分流
 *   q_cab: 输入/输出数组（各柜目标无功），有功优先超限时被缩减
 *   p_cab: 输入数组（各柜当前有功，带符号），无功优先超限时用于按比例分配 P_limit
 *   cab_num: 柜数
 *   grid_p: 关口当前有功 kW
 *   s_max_grid: 关口允许的最大视在 kVA（= Pmlmax * K1/100）
 *  sum_pv_p_ac: AC场景光伏实际有功总和 kW（无功优先分支从P_limit_total扣除PV后剩余分给PCS）
 *                DC场景恒传0.0（PV经PCS并网，pcs_output_power已含PV）
 *   返回: 0=未超限, 1=超限已缩减Q(有功优先=0), 2=超限已设置P_limit请求(无功优先=1)
 * 说明:
 *  - EnReactivePriority=0(有功优先): 等比例缩减 Q，保 P（安全底线，不使用sum_pv_p_ac）
 *   - EnReactivePriority=1(无功优先): 保留 Q 不缩减，pcs_p_limit_total=√(S_max²-Q²)−sum_pv_p_ac
 *     若≤0则PCS P_limit=0（全让给PV）；剩余按 p_cab 比例分配到 s_pf_p_limit_per_cab[]，
 *     置位 s_pf_p_limit_active。有功下发点（AC cabinet_loop / DC micro_dev_model）
 *     通过 pf_get_p_limit_per_cab() 主动查询并在下发 SET_ACT_POWER 前钳位。
 */
int pf_grid_cap_guard(double *q_cab, double *p_cab, int cab_num,
                      double grid_p, double s_max_grid, double sum_pv_p_ac);

/*
 * pf_cap_guard_standard: 标准场景(MODE_MICROGRID_NONE)单柜超容保护
 *   q_target: PF 计算出的无功目标
 *   q_result: 输出钳位后无功
 *   说明: 柜内钳位(额定=|dischargeThreshold|, 与微网 dischargeMaxPower 同源)
 *         → 关口HV视在校验(S_max=Pmlmax*K1/100)
 *         → 低压侧校验(双变压器, S_max_lv=PLVmlmax*K4/100, LV表在线时)
 *         → 无功优先超容时设 P_limit 请求(柜索引0, 由 update_cab_info
 *           有功下发点 def_set_pcs_power 前查询钳位)
 *   限制: 标准场景 PV 有功无聚合来源(meter_pv/PV_P/TOTAL_PV_POWER 均微网
 *         专属), sum_pv_p_ac 传 0——超容判定用关口实测P(已含PV影响)不受影响,
 *         仅无功优先时 P_limit 未扣 PV 份额, 钳位偏松
 *   返回: 0 成功, PF_ERR_RATED_POWER 额定功率非法, PF_ERR_PARAM 参数非法
 */
int pf_cap_guard_standard(double q_target, double *q_result);

/* ========== 关口级 P_limit 请求（供有功下发点主动查询） ========== */
/*
 * pf_get_p_limit_per_cab: 查询指定柜的 P_limit 上限
 *   idx: 柜索引（与 cabinet_info.cabinet_inside[i] 一致）
 *   返回: >=0=本柜 P_limit 上限(kW，仅限放电方向幅值); -1.0=当前无 P_limit 请求
 * 说明: 有功下发点在写 SET_ACT_POWER 前调用本接口，仅对放电方向(P>0)钳位
 */
double pf_get_p_limit_per_cab(int idx);

void pf_zero_all_cabs_q(void);

int pf_update_penalty_flag(void);

void pf_adjust_and_dispatch(time_t *last_adjust, pf_entry_tag_t entry_tag, double sum_pv_p_ac);

#ifdef __cplusplus
}
#endif

#endif
