<?php
/* Copyright (C) 2026 Alles Watt lauft
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 */

require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/equipmenttype.class.php';

/**
 * Class Equipment
 * Manages equipment instances (Sicherungsautomaten, FI-Schalter, etc.)
 */
class Equipment extends CommonObject
{
	public $element = 'equipment';
	public $table_element = 'kundenkarte_equipment';

	public $fk_carrier;
	public $fk_equipment_type;
	public $label;
	public $position_te;
	public $width_te;
	public $field_values;
	public $fk_product;
	public $fk_protection;      // FK to protection device (FI/RCD)
	public $protection_label;   // Label shown above equipment when in protection group
	public $note_private;
	public $status;

	public $date_creation;
	public $fk_user_creat;
	public $fk_user_modif;

	// Loaded objects
	public $type; // EquipmentType object
	public $type_label;
	public $type_label_short;
	public $type_color;
	public $type_picto;
	public $type_icon_file; // SVG/PNG schematic symbol (PDF)
	public $type_block_image; // Image for block display in SchematicEditor
	public $type_flow_direction; // Richtung: NULL, top_to_bottom, bottom_to_top
	public $type_terminal_position; // Terminal-Position: both, top_only, bottom_only
	public $product_ref;
	public $product_label;
	public $protection_device_label; // Label of the protection device

	/**
	 * Constructor
	 *
	 * @param DoliDB $db Database handler
	 */
	public function __construct($db)
	{
		$this->db = $db;
	}

	/**
	 * Create object in database
	 *
	 * @param User $user User that creates
	 * @return int Return integer <0 if KO, Id of created object if OK
	 */
	public function create($user)
	{
		global $conf;

		$error = 0;
		$now = dol_now();

		if (empty($this->fk_carrier) || empty($this->fk_equipment_type)) {
			$this->error = 'ErrorMissingParameters';
			return -1;
		}

		// Get default width from type if not set
		if (empty($this->width_te)) {
			$type = new EquipmentType($this->db);
			if ($type->fetch($this->fk_equipment_type) > 0) {
				$this->width_te = $type->width_te;
			} else {
				$this->width_te = 1;
			}
		}

		$this->db->begin();

		$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
		$sql .= "entity, fk_carrier, fk_equipment_type, label,";
		$sql .= " position_te, width_te, field_values, fk_product,";
		$sql .= " fk_protection, protection_label,";
		$sql .= " note_private, status,";
		$sql .= " date_creation, fk_user_creat";
		$sql .= ") VALUES (";
		$sql .= ((int) $conf->entity);
		$sql .= ", ".((int) $this->fk_carrier);
		$sql .= ", ".((int) $this->fk_equipment_type);
		$sql .= ", ".($this->label ? "'".$this->db->escape($this->label)."'" : "NULL");
		$sql .= ", ".((int) $this->position_te);
		$sql .= ", ".((int) $this->width_te);
		$sql .= ", ".($this->field_values ? "'".$this->db->escape($this->field_values)."'" : "NULL");
		$sql .= ", ".($this->fk_product > 0 ? ((int) $this->fk_product) : "NULL");
		$sql .= ", ".($this->fk_protection > 0 ? ((int) $this->fk_protection) : "NULL");
		$sql .= ", ".($this->protection_label ? "'".$this->db->escape($this->protection_label)."'" : "NULL");
		$sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
		$sql .= ", ".((int) ($this->status !== null ? $this->status : 1));
		$sql .= ", '".$this->db->idate($now)."'";
		$sql .= ", ".((int) $user->id);
		$sql .= ")";

		$resql = $this->db->query($sql);
		if (!$resql) {
			$error++;
			$this->errors[] = "Error ".$this->db->lasterror();
		}

		if (!$error) {
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
		}

		if ($error) {
			$this->db->rollback();
			return -1 * $error;
		} else {
			$this->db->commit();
			return $this->id;
		}
	}

	/**
	 * Load object from database
	 *
	 * @param int $id ID of record
	 * @return int Return integer <0 if KO, 0 if not found, >0 if OK
	 */
	public function fetch($id)
	{
		$sql = "SELECT e.*, t.label as type_label, t.label_short as type_label_short,";
		$sql .= " t.ref as type_ref, t.color as type_color, t.picto as type_picto, t.icon_file as type_icon_file,";
		$sql .= " t.block_image as type_block_image,";
		$sql .= " t.flow_direction as type_flow_direction, t.terminal_position as type_terminal_position,";
		$sql .= " t.terminals_config as terminals_config,";
		$sql .= " p.ref as product_ref, p.label as product_label,";
		$sql .= " prot.label as protection_device_label";
		$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as e";
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_type as t ON e.fk_equipment_type = t.rowid";
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON e.fk_product = p.rowid";
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as prot ON e.fk_protection = prot.rowid";
		$sql .= " WHERE e.rowid = ".((int) $id);

		$resql = $this->db->query($sql);
		if ($resql) {
			if ($this->db->num_rows($resql)) {
				$obj = $this->db->fetch_object($resql);

				$this->id = $obj->rowid;
				$this->entity = $obj->entity;
				$this->fk_carrier = $obj->fk_carrier;
				$this->fk_equipment_type = $obj->fk_equipment_type;
				$this->label = $obj->label;
				$this->position_te = $obj->position_te;
				$this->width_te = $obj->width_te;
				$this->field_values = $obj->field_values;
				$this->fk_product = $obj->fk_product;
				$this->fk_protection = $obj->fk_protection;
				$this->protection_label = $obj->protection_label;
				$this->note_private = $obj->note_private;
				$this->status = $obj->status;
				$this->date_creation = $this->db->jdate($obj->date_creation);
				$this->fk_user_creat = $obj->fk_user_creat;
				$this->fk_user_modif = $obj->fk_user_modif;

				$this->type_label = $obj->type_label;
				$this->type_label_short = $obj->type_label_short;
				$this->type_ref = $obj->type_ref;
				$this->type_color = $obj->type_color;
				$this->type_picto = $obj->type_picto;
				$this->type_icon_file = $obj->type_icon_file;
				$this->type_block_image = $obj->type_block_image;
				$this->type_flow_direction = $obj->type_flow_direction;
				$this->type_terminal_position = $obj->type_terminal_position ?: 'both';
				$this->terminals_config = $obj->terminals_config;
				$this->product_ref = $obj->product_ref;
				$this->product_label = $obj->product_label;
				$this->protection_device_label = $obj->protection_device_label;

				$this->db->free($resql);
				return 1;
			} else {
				$this->db->free($resql);
				return 0;
			}
		} else {
			$this->error = $this->db->lasterror();
			return -1;
		}
	}

	/**
	 * Update object in database
	 *
	 * @param User $user User that modifies
	 * @return int Return integer <0 if KO, >0 if OK
	 */
	public function update($user)
	{
		$error = 0;

		$this->db->begin();

		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
		$sql .= " fk_carrier = ".((int) $this->fk_carrier);
		$sql .= ", fk_equipment_type = ".((int) $this->fk_equipment_type);
		$sql .= ", label = ".($this->label ? "'".$this->db->escape($this->label)."'" : "NULL");
		$sql .= ", position_te = ".((int) $this->position_te);
		$sql .= ", width_te = ".((int) $this->width_te);
		$sql .= ", field_values = ".($this->field_values ? "'".$this->db->escape($this->field_values)."'" : "NULL");
		$sql .= ", fk_product = ".($this->fk_product > 0 ? ((int) $this->fk_product) : "NULL");
		$sql .= ", fk_protection = ".($this->fk_protection > 0 ? ((int) $this->fk_protection) : "NULL");
		$sql .= ", protection_label = ".($this->protection_label ? "'".$this->db->escape($this->protection_label)."'" : "NULL");
		$sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
		$sql .= ", status = ".((int) $this->status);
		$sql .= ", fk_user_modif = ".((int) $user->id);
		$sql .= " WHERE rowid = ".((int) $this->id);

		$resql = $this->db->query($sql);
		if (!$resql) {
			$error++;
			$this->errors[] = "Error ".$this->db->lasterror();
		}

		if ($error) {
			$this->db->rollback();
			return -1 * $error;
		} else {
			$this->db->commit();
			return 1;
		}
	}

	/**
	 * Delete object in database
	 *
	 * @param User $user User that deletes
	 * @return int Return integer <0 if KO, >0 if OK
	 */
	public function delete($user)
	{
		$error = 0;
		$this->db->begin();

		$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
		$resql = $this->db->query($sql);
		if (!$resql) {
			$error++;
			$this->errors[] = "Error ".$this->db->lasterror();
		}

		if ($error) {
			$this->db->rollback();
			return -1 * $error;
		} else {
			$this->db->commit();
			return 1;
		}
	}

	/**
	 * Fetch all equipment on a carrier
	 *
	 * @param int $carrierId Carrier ID
	 * @param int $activeOnly Only active equipment
	 * @return array Array of Equipment objects
	 */
	public function fetchByCarrier($carrierId, $activeOnly = 1)
	{
		$results = array();

		$sql = "SELECT e.*, t.label as type_label, t.label_short as type_label_short,";
		$sql .= " t.ref as type_ref, t.color as type_color, t.picto as type_picto, t.icon_file as type_icon_file,";
		$sql .= " t.block_image as type_block_image,";
		$sql .= " t.flow_direction as type_flow_direction, t.terminal_position as type_terminal_position,";
		$sql .= " t.terminals_config as terminals_config,";
		$sql .= " prot.label as protection_device_label";
		$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as e";
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_type as t ON e.fk_equipment_type = t.rowid";
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as prot ON e.fk_protection = prot.rowid";
		$sql .= " WHERE e.fk_carrier = ".((int) $carrierId);
		if ($activeOnly) {
			$sql .= " AND e.status = 1";
		}
		$sql .= " ORDER BY e.position_te ASC";

		$resql = $this->db->query($sql);
		if ($resql) {
			while ($obj = $this->db->fetch_object($resql)) {
				$eq = new Equipment($this->db);
				$eq->id = $obj->rowid;
				$eq->entity = $obj->entity;
				$eq->fk_carrier = $obj->fk_carrier;
				$eq->fk_equipment_type = $obj->fk_equipment_type;
				$eq->label = $obj->label;
				$eq->position_te = $obj->position_te;
				$eq->width_te = $obj->width_te;
				$eq->field_values = $obj->field_values;
				$eq->fk_product = $obj->fk_product;
				$eq->fk_protection = $obj->fk_protection;
				$eq->protection_label = $obj->protection_label;
				$eq->note_private = $obj->note_private;
				$eq->status = $obj->status;

				$eq->type_label = $obj->type_label;
				$eq->type_label_short = $obj->type_label_short;
				$eq->type_ref = $obj->type_ref;
				$eq->type_color = $obj->type_color;
				$eq->type_picto = $obj->type_picto;
				$eq->type_icon_file = $obj->type_icon_file;
				$eq->type_block_image = $obj->type_block_image;
				$eq->type_flow_direction = $obj->type_flow_direction;
				$eq->type_terminal_position = $obj->type_terminal_position ?: 'both';
				$eq->terminals_config = $obj->terminals_config;
				$eq->protection_device_label = $obj->protection_device_label;

				$results[] = $eq;
			}
			$this->db->free($resql);
		}

		return $results;
	}

	/**
	 * Fetch protection devices (FI/RCD) for an Anlage
	 * Protection devices are equipment types that can protect other equipment
	 *
	 * @param int $anlageId Anlage ID
	 * @return array Array of Equipment objects that are protection devices
	 */
	public function fetchProtectionDevices($anlageId)
	{
		$results = array();

		// Get all equipment for this anlage that have type with is_protection = 1
		// Or equipment types that are typically protection devices (FI, RCD, etc.)
		$sql = "SELECT e.*, t.label as type_label, t.label_short as type_label_short,";
		$sql .= " t.color as type_color, t.ref as type_ref, c.fk_anlage";
		$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as e";
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_type as t ON e.fk_equipment_type = t.rowid";
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_carrier as c ON e.fk_carrier = c.rowid";
		$sql .= " WHERE c.fk_anlage = ".((int) $anlageId);
		$sql .= " AND e.status = 1";
		// Filter for protection device types (FI, RCD, FI_LS, etc.)
		$sql .= " AND (t.ref LIKE '%FI%' OR t.ref LIKE '%RCD%' OR t.ref LIKE 'RCBO%')";
		$sql .= " ORDER BY c.position ASC, e.position_te ASC";

		$resql = $this->db->query($sql);
		if ($resql) {
			while ($obj = $this->db->fetch_object($resql)) {
				$eq = new Equipment($this->db);
				$eq->id = $obj->rowid;
				$eq->fk_carrier = $obj->fk_carrier;
				$eq->fk_equipment_type = $obj->fk_equipment_type;
				$eq->label = $obj->label;
				$eq->position_te = $obj->position_te;
				$eq->width_te = $obj->width_te;
				$eq->type_label = $obj->type_label;
				$eq->type_label_short = $obj->type_label_short;
				$eq->type_color = $obj->type_color;

				$results[] = $eq;
			}
			$this->db->free($resql);
		}

		return $results;
	}

	/**
	 * Duplicate this equipment (for "+" button)
	 *
	 * @param User $user User that creates
	 * @param int $carrierId Target carrier (0 = same carrier)
	 * @param int $position Target position (-1 = auto)
	 * @return int Return integer <0 if KO, Id of new object if OK
	 */
	public function duplicate($user, $carrierId = 0, $position = -1)
	{
		require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/equipmentcarrier.class.php';

		$newEquipment = new Equipment($this->db);
		$newEquipment->fk_carrier = $carrierId > 0 ? $carrierId : $this->fk_carrier;
		$newEquipment->fk_equipment_type = $this->fk_equipment_type;
		$newEquipment->label = $this->label;
		$newEquipment->width_te = $this->width_te;
		$newEquipment->field_values = $this->field_values;
		$newEquipment->fk_product = $this->fk_product;
		$newEquipment->note_private = $this->note_private;
		$newEquipment->status = 1;

		// Find position
		if ($position >= 0) {
			$newEquipment->position_te = $position;
		} else {
			// Auto-find next position after this equipment
			$carrier = new EquipmentCarrier($this->db);
			$carrier->fetch($newEquipment->fk_carrier);
			$carrier->fetchEquipment();

			// Try position right after this element
			$tryPos = $this->position_te + $this->width_te;
			if ($carrier->isPositionAvailable($tryPos, $this->width_te)) {
				$newEquipment->position_te = $tryPos;
			} else {
				// Find any free position
				$newEquipment->position_te = $carrier->getNextFreePosition($this->width_te);
				if ($newEquipment->position_te < 0) {
					$this->error = 'ErrorNoSpaceOnCarrier';
					return -1;
				}
			}
		}

		return $newEquipment->create($user);
	}

	/**
	 * Get field values as array
	 *
	 * @return array
	 */
	public function getFieldValues()
	{
		if (empty($this->field_values)) {
			return array();
		}
		$values = json_decode($this->field_values, true);
		return is_array($values) ? $values : array();
	}

	/**
	 * Set field values from array
	 *
	 * @param array $values Key-value pairs
	 * @return void
	 */
	public function setFieldValues($values)
	{
		$this->field_values = json_encode($values);
	}

	/**
	 * Get single field value
	 *
	 * @param string $fieldCode Field code
	 * @return mixed|null
	 */
	public function getFieldValue($fieldCode)
	{
		$values = $this->getFieldValues();
		return isset($values[$fieldCode]) ? $values[$fieldCode] : null;
	}

	/**
	 * Get label for SVG block display
	 * Combines fields with show_on_block = 1
	 *
	 * @return string Label to display on block (e.g. "B16")
	 */
	public function getBlockLabel()
	{
		$type = new EquipmentType($this->db);
		if ($type->fetch($this->fk_equipment_type) <= 0) {
			return $this->type_label_short ?: '';
		}

		$blockFields = $type->getBlockFields();
		if (empty($blockFields)) {
			return $this->type_label_short ?: '';
		}

		$values = $this->getFieldValues();
		$parts = array();

		foreach ($blockFields as $field) {
			if (isset($values[$field->field_code]) && $values[$field->field_code] !== '') {
				$parts[] = $values[$field->field_code];
			}
		}

		if (empty($parts)) {
			return $this->type_label_short ?: '';
		}

		return implode('', $parts);
	}

	/**
	 * Get color for SVG block
	 *
	 * @return string Hex color code
	 */
	public function getBlockColor()
	{
		if (!empty($this->type_color)) {
			return $this->type_color;
		}

		// Default color
		return '#3498db';
	}
}
