<?php
/**
 * BSW Integration for Gravity Forms and Pipedrive
 *
 * @package bsw-gravityforms-pipedrive
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
} // Exit if accessed directly

GFForms::include_feed_addon_framework();

require_once 'vendor/autoload.php';

use Pipedrive\Configuration;


/**
 * Class BSWGFPipedrive
 */
class BSWGFPipedrive extends GFFeedAddOn {

	/**
	 * Plugin version.
	 *
	 * @var string $_version Plugin version.
	 */
	protected $_version = BSWGRAVITYFORMSPIPEDRIVE_VERSION;

	/**
	 * Minimum Gravity Forms version required.
	 *
	 * @var string $_min_gravityforms_version Minimum Gravity Forms version required.
	 */
	protected $_min_gravityforms_version = '1.9';

	/**
	 * Plugin slug.
	 *
	 * @var string $_slug The unique identifier of this plugin.
	 */
	protected $_slug = 'pipedrive';

	/**
	 * Relative path to the plugin from the plugins folder.
	 *
	 * @var string $_path The full path to this class file.
	 */
	protected $_path = 'bsw-gravityforms-pipedrive/bsw-gravityforms-pipedrive.php';

	/**
	 * Full path to the plugin.
	 *
	 * @var string $_full_path The full path to the main plugin file.
	 */
	protected $_full_path = __FILE__;

	/**
	 * Title of the plugin.
	 *
	 * @var string $_title The title of the plugin.
	 */
	protected $_title = 'BSW Integration for Gravity Forms and Pipedrive';

	/**
	 * Short title of the plugin.
	 *
	 * @var string $_short_title The short title of the plugin.
	 */
	protected $_short_title = 'Pipedrive';

	/**
	 * Instance of this class.
	 *
	 * @var BSWGFPipedrive $_instance
	 */
	private static $_instance = null;

	/**
	 * Get an instance of this class.
	 *
	 * @return BSWGFPipedrive
	 */
	public static function get_instance() {
		if ( null === self::$_instance ) {
			self::$_instance = new BSWGFPipedrive();
		}

		return self::$_instance;
	}

	/**
	 * Handles hooks and loading of language files.
	 */
	public function init() {
		parent::init();
	}

	/**
	 * Return the plugin's icon for the plugin/form settings menu.
	 *
	 * @return string
	 */
	public function get_menu_icon() {

		return file_get_contents( $this->get_base_path() . '/images/menu-icon.svg' );
	}

	/**
	 * Setup columns for feed list table.
	 *
	 * @access public
	 * @return array
	 */
	public function feed_list_columns() {

		return array(
			'feedName' => __( 'Name', 'bsw-gravityforms-pipedrive' ),
		);
	}

	/**
	 * Configures the settings which should be rendered on the add-on settings tab.
	 *
	 * @return array
	 */
	public function plugin_settings_fields() {
		return array(
			array(
				'title'  => esc_html__( 'Pipedrive settings', 'bsw-gravityforms-pipedrive' ),
				'fields' => array(
					array(
						'name'              => 'api_token',
						'tooltip'           => esc_html__( 'Specify the access token to the Pipedrive REST API', 'bsw-gravityforms-pipedrive' ),
						'label'             => esc_html__( 'REST API access token', 'bsw-gravityforms-pipedrive' ),
						'type'              => 'text',
						'class'             => 'small',
						'feedback_callback' => array( $this, 'initialize_api' ),
					),
				),
			),
		);
	}

	/**
	 * Prepare standard fields for feed field mapping.
	 *
	 * @access public
	 * @return array
	 */
	public function contact_fields_for_feed_mapping() {

		return array(
			array(
				'name'          => 'first_name',
				'label'         => __( 'First name', 'bsw-gravityforms-pipedrive' ),
				'required'      => true,
				'field_type'    => array( 'name', 'text', 'hidden' ),
				'default_value' => $this->get_first_field_by_type( 'name', 3 ),
			),
			array(
				'name'          => 'last_name',
				'label'         => __( 'Last name', 'bsw-gravityforms-pipedrive' ),
				'required'      => true,
				'field_type'    => array( 'name', 'text', 'hidden' ),
				'default_value' => $this->get_first_field_by_type( 'name', 6 ),
			),
			array(
				'name'          => 'email_address',
				'label'         => __( 'E-mail address', 'bsw-gravityforms-pipedrive' ),
				'required'      => true,
				'field_type'    => array( 'email', 'hidden' ),
				'default_value' => $this->get_first_field_by_type( 'email' ),
			),
			array(
				'name'          => 'phone',
				'label'         => __( 'Phone number', 'bsw-gravityforms-pipedrive' ),
				'required'      => false,
				'field_type'    => array( 'phone', 'hidden' ),
				'default_value' => $this->get_first_field_by_type( 'phone' ),
			),
		);
	}

	/**
	 * Setup fields for feed settings.
	 *
	 * @access public
	 * @return array
	 */
	public function feed_settings_fields() {

		/* Build base fields array. */
		$base_fields = array(
			'title'  => '',
			'fields' => array(
				array(
					'name'          => 'feedName',
					'label'         => __( 'Feed Name', 'bsw-gravityforms-pipedrive' ),
					'type'          => 'text',
					'required'      => true,
					'default_value' => $this->get_default_feed_name(),
					'class'         => 'medium',
					'tooltip'       => '<h6>' . __( 'Name', 'bsw-gravityforms-pipedrive' ) . '</h6>' . __( 'Enter a feed name to uniquely identify this setup.', 'bsw-gravityforms-pipedrive' ),
				),
				array(
						'name'              => 'personal_api_token',
						'tooltip'           => esc_html__( 'Specify the access token to the Pipedrive REST API', 'bsw-gravityforms-pipedrive' ),
						'label'             => esc_html__( 'REST API access token', 'bsw-gravityforms-pipedrive' ),
						'type'              => 'text',
						'class'             => 'small',
						'description'       => esc_html__( 'If specified, this API token will be used instead of the global one.', 'bsw-gravityforms-pipedrive' ),
					),
			),
		);

		$person_fields = array(
			'title'  => __( 'Contact details', 'bsw-gravityforms-pipedrive' ),
			'fields' => array(
				array(
					'name'      => 'contactStandardFields',
					'label'     => __( 'Map Fields', 'bsw-gravityforms-pipedrive' ),
					'type'      => 'field_map',
					'field_map' => $this->contact_fields_for_feed_mapping(),
					'tooltip'   => '<h6>' . __( 'Map Fields', 'bsw-gravityforms-pipedrive' ) . '</h6>' . __( 'Select which Gravity Form fields pair with their respective Pipedrive fields.', 'bsw-gravityforms-pipedrive' ),
				),
			),
		);

		$lead_labels    = $this->get_lead_labels();
		$lead_tags      = array();
		$nb_lead_labels = count( $lead_labels );
		for ( $i = 0; $i < $nb_lead_labels; $i++ ) {
			$lead_tags[] = array(
				'label' => $lead_labels[ $i ]['name'],
				'value' => $lead_labels[ $i ]['id'],
			);
		}

		$lead_fields = array(
			'title'  => __( 'Lead details', 'bsw-gravityforms-pipedrive' ),
			'fields' => array(
				array(
					'name'     => 'lead_title',
					'label'    => __( 'Title prefix', 'bsw-gravityforms-pipedrive' ),
					'tooltip'  => '<h6>' . __( 'Title prefix', 'bsw-gravityforms-pipedrive' ) . '</h6>' . __( 'The prefix will be concatenated to the contact\'s first and last name in the form: "prefix - first name last name".', 'bsw-gravityforms-pipedrive' ),
					'type'     => 'text',
					'class'    => 'medium',
					'required' => true,
				),
				array(
					'name'     => 'lead_tags',
					'label'    => __( 'Tags', 'bsw-gravityforms-pipedrive' ),
					'type'     => 'select',
					'required' => true,
					'choices'  => $lead_tags,
				),
				array(
					'name'     => 'lead_channel',
					'label'    => __( 'Marketing channel id', 'bsw-gravityforms-pipedrive' ),
					'tooltip'  => '<h6>' . __( 'Marketing channel id', 'bsw-gravityforms-pipedrive' ) . '</h6>' . __( 'Should be an integer', 'bsw-gravityforms-pipedrive' ),
					'type'     => 'text',
					'required' => false,
				),
			),
		);

		$activity_fields = array(
			'title'  => __( 'Activity details', 'bsw-gravityforms-pipedrive' ),
			'fields' => array(
				array(
					'name'     => 'activity_title',
					'label'    => __( 'Activity title', 'bsw-gravityforms-pipedrive' ),
					'type'     => 'text',
					'class'    => 'medium',
					'required' => true,
				),
				array(
					'name'     => 'activity_type',
					'label'    => __( 'Activity type', 'bsw-gravityforms-pipedrive' ),
					'type'     => 'select',
					'required' => true,
					'choices'  => array(
						array(
							'label' => __( 'Task', 'bsw-gravityforms-pipedrive' ),
							'value' => 'task',
						),
						array(
							'label' => __( 'Call', 'bsw-gravityforms-pipedrive' ),
							'value' => 'call',
						),
						array(
							'label' => __( 'Meeting', 'bsw-gravityforms-pipedrive' ),
							'value' => 'meeting',
						),
					),
				),
				array(
					'name'     => 'activity_due_date',
					'label'    => __( 'Activity due date', 'bsw-gravityforms-pipedrive' ),
					'type'     => 'select',
					'class'    => 'medium',
					'required' => true,
					'choices'  => array(
						array(
							'label' => __( 'Now', 'bsw-gravityforms-pipedrive' ),
							'value' => 'now',
						),
						array(
							'label' => __( 'In a day', 'bsw-gravityforms-pipedrive' ),
							'value' => '+1 day',
						),
						array(
							'label' => __( 'In two days', 'bsw-gravityforms-pipedrive' ),
							'value' => '+2 days',
						),
						array(
							'label' => __( 'In three days', 'bsw-gravityforms-pipedrive' ),
							'value' => '+3 days',
						),
						array(
							'label' => __( 'In a week', 'bsw-gravityforms-pipedrive' ),
							'value' => '+1 week',
						),
						array(
							'label' => __( 'In two weeks', 'bsw-gravityforms-pipedrive' ),
							'value' => '+2 weeks',
						),
						array(
							'label' => __( 'In three weeks', 'bsw-gravityforms-pipedrive' ),
							'value' => '+3 weeks',
						),
						array(
							'label' => __( 'In a month', 'bsw-gravityforms-pipedrive' ),
							'value' => '+1 month',
						),
						array(
							'label' => __( 'In two months', 'bsw-gravityforms-pipedrive' ),
							'value' => '+2 months',
						),
						array(
							'label' => 'Dans trois mois',
							'value' => '+3 months',
						),
					),
				),
				array(
					'name'       => 'note_template',
					'label'      => __( 'Note', 'bsw-gravityforms-pipedrive' ),
					'type'       => 'textarea',             // version native GF
					'use_editor' => true,
					'required'   => true,
					'default_value' => '{all_fields}'
				),
			),
		);

		// Section dédiée à la logique conditionnelle (Gravity Forms native)
		$conditional_fields = array(
			'title'  => __( 'Conditional Logic', 'bsw-gravityforms-pipedrive' ),
			'fields' => array(
				array(
					'name'    => 'feed_condition',
					'label'   => __( 'Conditional Logic', 'bsw-gravityforms-pipedrive' ),
					'type'    => 'feed_condition',
					'tooltip' => '<h6>' . __( 'Conditional Logic', 'bsw-gravityforms-pipedrive' ) . '</h6>' . __( 'Enable and configure when this feed should run based on form field values.', 'bsw-gravityforms-pipedrive' ),
				),
			),
		);

		return array( $base_fields, $person_fields, $lead_fields, $activity_fields, $conditional_fields );
	}

	/**
	 * Get the Pipedrive lead labels.
	 *
	 * @return array
	 */
	protected function get_lead_labels() {
		$api_token = $this->get_plugin_setting( 'api_token' );
		$config    = ( new Pipedrive\Configuration() )->setApiKey( 'api_token', $api_token );

		$api_instance = new Pipedrive\Api\LeadLabelsApi(
			new GuzzleHttp\Client(),
			$config
		);

		try {
			$result = $api_instance->getLeadLabels();
			if ( $result['success'] ) {
				$this->log_debug( __METHOD__ . '(): API credentials are valid.' );

				return $result['data'];
			} else {
				$this->log_error( __METHOD__ . '(): API credentials are invalid; ' . $result['error'] );

				return array();
			}
		} catch ( Exception $e ) {
			$this->log_error( __METHOD__ . '(): API credentials are invalid; ' . $e->getMessage() );

			return array();
		}
	}

	/**
	 * Saves form data to Pipedrive.
	 * cf. https://github.com/pipedrive/client-php
	 *
	 * @access public
	 *
	 * @param array $feed The feed object to be processed.
	 * @param array $entry The entry object to be processed.
	 * @param array $form The form object currently being processed.
	 */
	public function process_feed( $feed, $entry, $form ) {
		$this->log_debug( __METHOD__ . '(): Processing feed.' );

		// Respecter la logique conditionnelle du feed (si disponible dans le framework)
		if ( method_exists( $this, 'is_feed_condition_met' ) && ! $this->is_feed_condition_met( $feed, $form, $entry ) ) {
			$this->log_debug( __METHOD__ . '(): Feed condition not met, skipping.' );

			return null;
		}

		$personal_api_token = rgars( $feed, 'meta/personal_api_token' );
		if ( $personal_api_token ) {
			$api_token = $personal_api_token;
		} else {
			$api_token = $this->get_plugin_setting( 'api_token' );
		}
		$config    = ( new Pipedrive\Configuration() )->setApiKey( 'api_token', $api_token );

		$api_instance = new Pipedrive\Api\PersonsApi( new GuzzleHttp\Client(), $config );

		$contact_standard_fields = $this->get_field_map_fields( $feed, 'contactStandardFields' );
		$first_name              = $this->get_field_value( $form, $entry, $contact_standard_fields['first_name'] );
		$last_name               = $this->get_field_value( $form, $entry, $contact_standard_fields['last_name'] );
		$default_email           = $this->get_field_value( $form, $entry, $contact_standard_fields['email_address'] );
		$phone                   = $this->get_field_value( $form, $entry, $contact_standard_fields['phone'] );

		// If the name is empty, exit.
		if ( rgblank( $first_name ) || rgblank( $last_name ) ) {
			$this->add_feed_error( esc_html__( 'Contact could not be created as first and/or last name were not provided.', 'bsw-gravityforms-pipedrive' ), $feed, $entry, $form );

			return null;
		}

		// Search for the person.
		$person_search = $api_instance->searchPersons( $default_email );

		if ( $person_search['data']['items'] ) {
			$person_id = $person_search['data']['items'][0]['item']['id'];
		} else {
			$person_id = null;
		}

		$new_person = new \Pipedrive\Model\NewPerson();
		$new_person->setName( $first_name . ' ' . $last_name );
		$new_person->setEmail( $default_email );
		if ( $phone ) {
			$new_person->setPhone( $phone );
		}

		// Update the person if it exists.
		if ( $person_id ) {
			try {
				$result_update_person = $api_instance->updatePerson( $person_id, $new_person );
			} catch ( Exception $e ) {
				$this->log_debug( __METHOD__ . '(): Error when calling PersonsApi->updatePerson: ' . $e->getMessage() );

				return null;
			}
		} else {
			// Create the person if it doesn't exist.
			try {
				$result_add_person = $api_instance->addPerson( $new_person );
				$person_id         = $result_add_person['data']['id'];
			} catch ( Exception $e ) {
				$this->log_debug( __METHOD__ . '(): Error when calling PersonsApi->addPerson: ' . $e->getMessage() );

				return null;
			}
		}

		// Create the lead.
		$api_instance     = new Pipedrive\Api\LeadsApi( new GuzzleHttp\Client(), $config );
		$add_lead_request = new \Pipedrive\Model\AddLeadRequest();

		$lead_title   = rgars( $feed, 'meta/lead_title' );
		$lead_channel = rgars( $feed, 'meta/lead_channel' );
		$add_lead_request->setPersonId( $person_id );
		$add_lead_request->setTitle( $lead_title . ' - ' . $first_name . ' ' . $last_name );
		$add_lead_request->setLabelIds( array( rgars( $feed, 'meta/lead_tags' ) ) );
		if ( $lead_channel ) {
			$add_lead_request->setChannel( intval( $lead_channel ) );
		}

		try {
			$result_add_lead = $api_instance->addLead( $add_lead_request );
			$lead_id         = $result_add_lead['data']['id'];
		} catch ( Exception $e ) {
			$this->log_debug( __METHOD__ . '(): Error when calling LeadsApi->addLead: ' . $e->getMessage() );

			return null;
		}

		// Create the note.
		// Récupère et rend le template de note (nouveau comportement)
		$note_template = rgars( $feed, 'meta/note_template' );
		$note          = '';
		if ( $note_template ) {
			$note = GFCommon::replace_variables( $note_template, $form, $entry );
		}
		if ( $note ) {
			$api_instance     = new Pipedrive\Api\NotesApi( new GuzzleHttp\Client(), $config );
			$add_note_request = new \Pipedrive\Model\AddNoteRequest();

			$add_note_request->setLeadId( $result_add_lead['data']['id'] );
			$add_note_request->setContent( $note );

			try {
				$result_add_note = $api_instance->addNote( $add_note_request );
			} catch ( Exception $e ) {
				$this->log_debug( __METHOD__ . '(): Error when calling NotesApi->addNote: ' . $e->getMessage() );

				return null;
			}
		}

		// Create the activity.

		$api_instance         = new Pipedrive\Api\ActivitiesApi( new GuzzleHttp\Client(), $config );
		$add_activity_request = new \Pipedrive\Model\ActivityPostObject();

		$activity_title    = rgars( $feed, 'meta/activity_title' );
		$activity_type     = rgars( $feed, 'meta/activity_type' );
		$activity_due_date = rgars( $feed, 'meta/activity_due_date' );

		$add_activity_request->setSubject( $activity_title );
		$add_activity_request->setLeadId( $lead_id );
		$add_activity_request->setPersonId( $person_id );
		$add_activity_request->setType( $activity_type );
		$add_activity_request->setDueDate( gmdate( 'Y-m-d', strtotime( $activity_due_date ) ) );

		try {
			$result_add_activity = $api_instance->addActivity( $add_activity_request );
		} catch ( Exception $e ) {
			$this->log_debug( __METHOD__ . '(): Error when calling ActivitiesApi->addActivity: ' . $e->getMessage() );

			return null;
		}
	}

	/**
	 * Initializes Pipedrive API if credentials are valid.
	 *
	 * @access public
	 * @return bool
	 */
	public function initialize_api() {

		/* Get the plugin settings */
		$settings = $this->get_plugin_settings();

		/* If any of the account information fields are empty, return null. */
		if ( empty( $settings['api_token'] ) ) {
			return null;
		}

		$this->log_debug( __METHOD__ . "(): Validating API info for {$settings['api_token']} ." );

		$lead_labels = $this->get_lead_labels();
		if ( ! $lead_labels ) {
			$this->log_error( __METHOD__ . '(): API credentials are invalid.' );

			return false;
		} else {
			$this->log_debug( __METHOD__ . '(): API credentials are valid.' );

			return true;
		}
	}
}
