<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Catalog_admin extends CI_Controller
{
	private $catalog_config;

	public function __construct()
	{
		parent::__construct();
		$this->load->model('Catalog_model', 'catalog');
		$this->load->helper(array('url', 'download'));
		$this->load->library('catalog_auth');
		$this->load->library('catalog_notifier');
		$this->load->library('simple_pdf');
		$this->load->config('catalog');
		$this->catalog_config = array(
			'catalog_name' => $this->config->item('catalog_name'),
			'catalog_default_employee_id' => $this->config->item('catalog_default_employee_id'),
			'catalog_default_location_id' => $this->config->item('catalog_default_location_id'),
			'catalog_token_bytes' => $this->config->item('catalog_token_bytes'),
			'catalog_currency_symbol' => $this->config->item('catalog_currency_symbol')
		);
		$this->catalog->ensure_schema();
		$this->catalog_auth->require_login();
		$this->catalog_auth->require_permission('can_access_panel');
	}

	public function index()
	{
		$permissions = $this->catalog_auth->permissions();
		if (!empty($permissions['can_view_dashboard']))
		{
			redirect('catalog/admin/dashboard');
			return;
		}
		if (!empty($permissions['can_manage_links']))
		{
			redirect('catalog/admin/links');
			return;
		}
		if (!empty($permissions['can_view_orders']))
		{
			redirect('catalog/admin/orders');
			return;
		}
		if (!empty($permissions['can_manage_inventory']))
		{
			redirect('catalog/admin/inventory');
			return;
		}
		if (!empty($permissions['can_manage_catalog']))
		{
			redirect('catalog/admin/settings');
			return;
		}
		if (!empty($permissions['can_manage_permissions']))
		{
			redirect('catalog/admin/employees');
			return;
		}

		show_error('No tienes permisos para ver ningun modulo del panel.', 403);
	}

	public function dashboard()
	{
		$this->catalog_auth->require_permission('can_view_dashboard');
		$this->render_admin('dashboard');
	}

	public function links()
	{
		$this->catalog_auth->require_permission('can_manage_links');
		$this->render_admin('links');
	}

	public function orders()
	{
		$this->catalog_auth->require_permission('can_view_orders');
		$this->render_admin('orders');
	}

	public function employees()
	{
		$this->catalog_auth->require_permission('can_manage_permissions');
		$this->render_admin('employees');
	}

	public function inventory()
	{
		$this->catalog_auth->require_permission('can_manage_inventory');
		$this->render_admin('inventory');
	}

	public function settings()
	{
		$this->catalog_auth->require_permission('can_manage_catalog');
		$this->render_admin('settings');
	}

	public function preview()
	{
		$this->catalog_auth->require_permission('can_manage_catalog');
		$this->render_admin('preview');
	}

	public function preview_disabled_page()
	{
		$this->catalog_auth->require_permission('can_manage_catalog');

		$location_id = (int) $this->catalog_config['catalog_default_location_id'];
		$public_catalog_url = '';
		$public_link = $this->catalog->get_latest_active_public_link();
		if ($public_link && !empty($public_link['token']))
		{
			$public_catalog_url = site_url('catalog/l/' . rawurlencode((string) $public_link['token']));
		}

		$data = array(
			'catalog_name' => $this->catalog_config['catalog_name'],
			'logo_file_id' => $this->catalog->get_company_logo_file_id($location_id),
			'message' => $this->catalog->get_setting('disabled_link_message', 'Este link fue desactivado por la empresa. Si necesitas ayuda, comunicate con nuestro equipo.'),
			'title' => 'Link desactivado',
			'public_catalog_url' => $public_catalog_url
		);

		$this->load->view('catalog/link_disabled', $data);
	}

	public function preview_thanks_page()
	{
		$this->catalog_auth->require_permission('can_manage_catalog');

		$location_id = (int) $this->catalog_config['catalog_default_location_id'];
		$public_catalog_url = '';
		$preview_token = '';
		$public_link = $this->catalog->get_latest_active_public_link();
		if ($public_link)
		{
			$preview_token = !empty($public_link['token']) ? (string) $public_link['token'] : '';
			if (!empty($public_link['location_id']))
			{
				$location_id = (int) $public_link['location_id'];
			}
			if ($preview_token !== '')
			{
				$public_catalog_url = site_url('catalog/l/' . rawurlencode($preview_token));
			}
		}

		$data = array(
			'catalog_name' => $this->catalog_config['catalog_name'],
			'currency_symbol' => $this->catalog_config['catalog_currency_symbol'],
			'link' => array('token' => $preview_token),
			'order' => array('id' => 9999),
			'items' => array(),
			'thanks_message' => $this->catalog->get_setting('thank_you_message', 'Gracias por su pedido. Pronto nos comunicaremos con usted.'),
			'logo_file_id' => $this->catalog->get_company_logo_file_id($location_id),
			'download_url' => $public_catalog_url !== '' ? $public_catalog_url : '#',
			'back_url' => $public_catalog_url !== '' ? $public_catalog_url : '#'
		);

		$this->load->view('catalog/order_thanks', $data);
	}

	public function catalog()
	{
		$this->catalog_auth->require_permission('can_manage_catalog');
		redirect('catalog/admin/settings');
	}

	public function templates($edit_template_id = 0)
	{
		$this->catalog_auth->require_permission('can_manage_catalog');
		$edit_template = NULL;
		if ((int) $edit_template_id > 0)
		{
			$edit_template = $this->catalog->get_template((int) $edit_template_id);
		}

		$this->render_admin('templates', array(
			'edit_template' => $edit_template
		));
	}

	public function create_link()
	{
		$this->catalog_auth->require_permission('can_manage_links');

		$customer_id = (int) $this->input->post('customer_id');
		$expires_at = trim((string) $this->input->post('expires_at'));
		$expires_days = trim((string) $this->input->post('expires_days'));
		$employee_id = (int) $this->input->post('employee_id');
		$location_id = (int) $this->input->post('location_id');
		$current_user = $this->catalog_auth->user();
		$effective_employee_id = $employee_id > 0 ? $employee_id : (int) $this->catalog_config['catalog_default_employee_id'];
		$allowed_location_ids = $this->catalog->get_location_ids_for_employee($effective_employee_id);
		if (empty($allowed_location_ids))
		{
			show_error('El empleado seleccionado no tiene sucursales activas en PHPPOS.', 400);
			return;
		}
		$effective_location_id = $location_id > 0 ? $location_id : (int) $this->catalog_config['catalog_default_location_id'];
		if (!in_array($effective_location_id, $allowed_location_ids, TRUE))
		{
			$default_location_id = (int) $this->catalog_config['catalog_default_location_id'];
			if (in_array($default_location_id, $allowed_location_ids, TRUE))
			{
				$effective_location_id = $default_location_id;
			}
			else
			{
				$effective_location_id = (int) $allowed_location_ids[0];
			}
		}

		$customer = NULL;
		if ($customer_id > 0)
		{
			$customer = $this->catalog->get_customer($customer_id);
		}
		if ($customer_id > 0 && !$customer)
		{
			show_error('Cliente invalido.', 400);
			return;
		}

		$expires_datetime = '';
		if (in_array($expires_days, array('1', '2', '3', '4', '5', '6'), TRUE))
		{
			$expires_datetime = date('Y-m-d H:i:s', strtotime('+' . (int) $expires_days . ' days'));
		}
		elseif ($expires_at !== '')
		{
			$expires_datetime = date('Y-m-d H:i:s', strtotime($expires_at));
		}

		if ($expires_datetime === '')
		{
			show_error('Fecha de expiracion requerida.', 400);
			return;
		}

		if (strtotime($expires_datetime) <= time())
		{
			show_error('La fecha de expiracion debe ser futura.', 400);
			return;
		}

		$token = bin2hex(random_bytes((int) $this->catalog_config['catalog_token_bytes']));
		if ($customer && $customer_id > 0)
		{
			$customer_name = trim($customer['company_name']) !== ''
				? $customer['company_name']
				: trim($customer['first_name'] . ' ' . $customer['last_name']);
		}
		else
		{
			$customer_name = 'Catalogo publico';
		}
		$created_by = 'usuario_desconocido';
		if (is_array($current_user))
		{
			$display_name = trim(isset($current_user['name']) ? (string) $current_user['name'] : '');
			$username = trim(isset($current_user['username']) ? (string) $current_user['username'] : '');
			$base_user = $display_name !== '' ? $display_name : ($username !== '' ? $username : 'usuario');
			$created_by = $base_user;
		}

		$link_id = $this->catalog->create_link(array(
			'token' => $token,
			'customer_id' => $customer_id > 0 ? $customer_id : 0,
			'customer_name' => $customer_name,
			'tier_id' => ($customer && !empty($customer['tier_id'])) ? (int) $customer['tier_id'] : NULL,
			'employee_id' => $effective_employee_id,
			'location_id' => $effective_location_id,
			'expires_at' => $expires_datetime,
			'created_at' => date('Y-m-d H:i:s'),
			'created_by' => $created_by,
			'status' => 1
		));

		$link = $this->catalog->get_link_by_id($link_id);
		$data = array(
			'catalog_name' => $this->catalog_config['catalog_name'],
			'public_url' => site_url('catalog/l/' . $link['token']),
			'link' => $link
		);

		$this->catalog_notifier->send('link_created', array(
			'link_id' => (int) $link['id'],
			'token' => (string) $link['token'],
			'public_url' => (string) $data['public_url'],
			'customer_id' => (int) $link['customer_id'],
			'customer_name' => (string) $link['customer_name'],
			'employee_id' => !empty($link['employee_id']) ? (int) $link['employee_id'] : 0,
			'location_id' => !empty($link['location_id']) ? (int) $link['location_id'] : 0,
			'expires_at' => (string) $link['expires_at'],
			'created_at' => (string) $link['created_at'],
			'created_by' => (string) $link['created_by']
		));

		$this->load->view('catalog/admin_link_created', $data);
	}

	public function delete_link($link_id)
	{
		$this->catalog_auth->require_permission('can_manage_links');

		$link_id = (int) $link_id;
		if ($link_id <= 0)
		{
			show_error('Link invalido.', 400);
			return;
		}

		$link = $this->catalog->get_link_by_id($link_id);
		if (!$link)
		{
			show_404();
			return;
		}

		$this->catalog->deactivate_link($link_id);
		$this->session->set_flashdata('catalog_notice', 'Link desactivado correctamente.');
		redirect('catalog/admin/links');
	}

	public function order_detail($order_id)
	{
		$this->catalog_auth->require_permission('can_view_orders');

		$order = $this->catalog->get_order((int) $order_id);
		if (!$order)
		{
			show_404();
			return;
		}

		$data = array(
			'order' => $order,
			'items' => $this->catalog->get_order_items((int) $order_id),
			'currency_symbol' => $this->catalog_config['catalog_currency_symbol']
		);

		$this->load->view('catalog/admin_order_detail', $data);
	}

	public function export_order_csv($order_id)
	{
		$this->catalog_auth->require_permission('can_download_reports');

		$order = $this->catalog->get_order((int) $order_id);
		if (!$order)
		{
			show_404();
			return;
		}

		$items = $this->catalog->get_order_items((int) $order_id);
		$lines = array();
		$lines[] = 'order_id,sale_id,cliente,fecha,item_id,item,cantidad,precio_unitario,total_linea,nota';

		foreach ($items as $item)
		{
			$line = array(
				$order['id'],
				$order['sale_id'],
				$this->csv_escape($order['customer_name']),
				$order['ordered_at'],
				$item['item_id'],
				$this->csv_escape($item['item_name']),
				$item['quantity'],
				$item['unit_price'],
				$item['line_total'],
				$this->csv_escape($order['customer_note'])
			);
			$lines[] = implode(',', $line);
		}

		$csv = implode("\n", $lines);
		force_download('pedido_' . $order['id'] . '.csv', $csv);
	}

	public function export_order_pdf($order_id)
	{
		$this->catalog_auth->require_permission('can_download_reports');

		$order = $this->catalog->get_order((int) $order_id);
		if (!$order)
		{
			show_404();
			return;
		}

		$items = $this->catalog->get_order_items((int) $order_id);
		$this->load->library('m_pdf');
		$currency = (string) $this->catalog_config['catalog_currency_symbol'];
		$receipt_no = 'REC-' . str_pad((string) (int) $order['id'], 6, '0', STR_PAD_LEFT);
		$logo_data_uri = '';

		$location_id = !empty($order['location_id'])
			? (int) $order['location_id']
			: (int) $this->catalog_config['catalog_default_location_id'];
		$logo_file_id = $this->catalog->get_company_logo_file_id($location_id);
		if (!empty($logo_file_id))
		{
			$logo = $this->catalog->get_image_file((int) $logo_file_id);
			if ($logo && !empty($logo['file_data']))
			{
				$mime = 'image/jpeg';
				$file_name = strtolower((string) $logo['file_name']);
				if (preg_match('/\.png$/', $file_name))
				{
					$mime = 'image/png';
				}
				elseif (preg_match('/\.gif$/', $file_name))
				{
					$mime = 'image/gif';
				}
				elseif (preg_match('/\.webp$/', $file_name))
				{
					$mime = 'image/webp';
				}

				$logo_data_uri = 'data:' . $mime . ';base64,' . base64_encode($logo['file_data']);
			}
		}

		$rows_html = '';
		foreach ($items as $item)
		{
			$rows_html .= '<tr>'
				. '<td>' . htmlspecialchars((string) $item['item_name'], ENT_QUOTES, 'UTF-8') . '</td>'
				. '<td class="text-right">' . number_format((float) $item['quantity'], 2) . '</td>'
				. '<td class="text-right">' . $currency . number_format((float) $item['unit_price'], 2) . '</td>'
				. '<td class="text-right strong">' . $currency . number_format((float) $item['line_total'], 2) . '</td>'
				. '</tr>';
		}

		$note_html = '';
		if (trim((string) $order['customer_note']) !== '')
		{
			$note_html = '<div class="note"><span class="label">Nota:</span> '
				. nl2br(htmlspecialchars((string) $order['customer_note'], ENT_QUOTES, 'UTF-8'))
				. '</div>';
		}

		$logo_html = '';
		if ($logo_data_uri !== '')
		{
			$logo_html = '<img class="logo" src="' . $logo_data_uri . '" alt="Logo">';
		}

		$html = '
		<!doctype html>
		<html>
		<head>
			<meta charset="utf-8">
			<style>
				body { font-family: DejaVu Sans, sans-serif; color:#0f172a; font-size:12px; }
				.sheet { border:1px solid #e2e8f0; border-radius:10px; padding:18px; }
				.head { display:table; width:100%; margin-bottom:14px; }
				.head-left, .head-right { display:table-cell; vertical-align:top; }
				.head-right { text-align:right; }
				.logo { width:130px; max-height:62px; object-fit:contain; }
				.title { font-size:22px; font-weight:700; color:#0f766e; margin:6px 0 4px; }
				.muted { color:#64748b; }
				.badge { display:inline-block; padding:4px 8px; background:#eef2ff; border:1px solid #c7d2fe; border-radius:16px; font-size:11px; }
				.meta { margin:10px 0 14px; }
				.meta-row { margin:2px 0; }
				table { width:100%; border-collapse:collapse; }
				th { text-align:left; background:#f8fafc; color:#475569; font-size:11px; border-bottom:1px solid #cbd5e1; padding:8px; }
				td { border-bottom:1px solid #e2e8f0; padding:8px; }
				.text-right { text-align:right; }
				.strong { font-weight:700; }
				.summary { width:300px; margin-left:auto; margin-top:10px; }
				.summary td { border:none; padding:4px 0; }
				.summary .total { border-top:1px solid #cbd5e1; padding-top:8px; font-size:14px; font-weight:700; }
				.note { margin-top:14px; background:#fffbeb; border:1px solid #fde68a; border-radius:8px; padding:10px; }
				.label { font-weight:700; }
				.footer { margin-top:14px; text-align:center; color:#475569; font-size:11px; }
			</style>
		</head>
		<body>
			<div class="sheet">
				<div class="head">
					<div class="head-left">
						' . $logo_html . '
						<div class="title">RECIBO DE PEDIDO</div>
						<div class="muted">Comprobante de compra</div>
					</div>
					<div class="head-right">
						<div class="badge">Recibo ' . htmlspecialchars($receipt_no, ENT_QUOTES, 'UTF-8') . '</div>
					</div>
				</div>

				<div class="meta">
					<div class="meta-row"><span class="label">Pedido:</span> #' . (int) $order['id'] . '</div>
					<div class="meta-row"><span class="label">Venta suspendida:</span> #' . (int) $order['sale_id'] . '</div>
					<div class="meta-row"><span class="label">Cliente:</span> ' . htmlspecialchars((string) $order['customer_name'], ENT_QUOTES, 'UTF-8') . '</div>
					<div class="meta-row"><span class="label">Fecha:</span> ' . htmlspecialchars((string) $order['ordered_at'], ENT_QUOTES, 'UTF-8') . '</div>
					<div class="meta-row"><span class="label">Sucursal:</span> ' . (int) $order['location_id'] . ' | <span class="label">Empleado:</span> ' . (int) $order['employee_id'] . '</div>
				</div>

				<table>
					<thead>
						<tr>
							<th style="width:52%;">Producto</th>
							<th style="width:16%;" class="text-right">Cantidad</th>
							<th style="width:16%;" class="text-right">Precio</th>
							<th style="width:16%;" class="text-right">Total</th>
						</tr>
					</thead>
					<tbody>' . $rows_html . '</tbody>
				</table>

				<table class="summary">
					<tr><td>Subtotal</td><td class="text-right">' . $currency . number_format((float) $order['subtotal'], 2) . '</td></tr>
					<tr><td>Impuesto</td><td class="text-right">' . $currency . number_format((float) $order['tax'], 2) . '</td></tr>
					<tr class="total"><td>TOTAL</td><td class="text-right">' . $currency . number_format((float) $order['total'], 2) . '</td></tr>
				</table>

				' . $note_html . '
				<div class="footer">Reporte generado por Catalogo</div>
			</div>
		</body>
		</html>';

		$this->m_pdf->generate_pdf($html, TRUE, 'pedido_' . (int) $order['id'] . '.pdf');
	}

	public function process_order($order_id)
	{
		$this->catalog_auth->require_permission('can_view_orders');

		$order_id = (int) $order_id;
		if ($order_id <= 0)
		{
			show_error('Pedido invalido.', 400);
			return;
		}

		$result = $this->catalog->process_order_to_suspended_sale($order_id);
		if (!$result['ok'])
		{
			$this->session->set_flashdata('catalog_notice', $result['message']);
			redirect('catalog/admin/orders');
			return;
		}

		if (!empty($result['already_processed']))
		{
			$this->session->set_flashdata('catalog_notice', 'Este pedido ya estaba procesado en PHPPOS. Venta suspendida #' . (int) $result['sale_id'] . '.');
		}
		else
		{
			$this->session->set_flashdata('catalog_notice', 'Pedido procesado correctamente. Venta suspendida #' . (int) $result['sale_id'] . '.');
		}

		redirect('catalog/admin/orders');
	}

	public function assign_order_customer($order_id)
	{
		$this->catalog_auth->require_permission('can_view_orders');

		$order_id = (int) $order_id;
		$customer_id = (int) $this->input->post('customer_id');
		$is_ajax = $this->input->is_ajax_request();

		if ($order_id <= 0 || $customer_id <= 0)
		{
			$message = 'Pedido o cliente invalido.';
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => $message)));
				return;
			}
			show_error($message, 400);
			return;
		}

		$default_employee_id = (int) $this->catalog_config['catalog_default_employee_id'];
		$default_location_id = (int) $this->catalog_config['catalog_default_location_id'];
		$current_user = $this->catalog_auth->user();
		if (is_array($current_user) && !empty($current_user['person_id']))
		{
			$current_employee_id = (int) $current_user['person_id'];
			$current_locations = $this->catalog->get_location_ids_for_employee($current_employee_id);
			if (!empty($current_locations))
			{
				$default_employee_id = $current_employee_id;
				if (!in_array($default_location_id, $current_locations, TRUE))
				{
					$default_location_id = (int) $current_locations[0];
				}
			}
		}

		$result = $this->catalog->assign_order_customer($order_id, $customer_id, $default_employee_id, $default_location_id);
		if (!$result['ok'])
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode($result));
				return;
			}
			$this->session->set_flashdata('catalog_notice', $result['message']);
			redirect('catalog/admin/orders');
			return;
		}

		if ($is_ajax)
		{
			$this->output
				->set_content_type('application/json')
				->set_output(json_encode(array(
					'ok' => TRUE,
					'message' => 'Cliente asignado correctamente.',
					'customer_id' => (int) $result['customer_id'],
					'customer_name' => (string) $result['customer_name'],
					'employee_id' => !empty($result['employee_id']) ? (int) $result['employee_id'] : 0,
					'location_id' => !empty($result['location_id']) ? (int) $result['location_id'] : 0
				)));
			return;
		}

		$this->session->set_flashdata('catalog_notice', 'Cliente asignado correctamente.');
		redirect('catalog/admin/orders');
	}

	public function delete_order($order_id)
	{
		$this->catalog_auth->require_permission('can_view_orders');

		$order_id = (int) $order_id;
		if ($order_id <= 0)
		{
			show_error('Pedido invalido.', 400);
			return;
		}

		$order = $this->catalog->get_order($order_id);
		if (!$order)
		{
			show_404();
			return;
		}

		$this->catalog->delete_order($order_id);
		$this->session->set_flashdata('catalog_notice', 'Pedido eliminado correctamente.');
		redirect('catalog/admin/orders');
	}

	public function save_permissions()
	{
		$this->catalog_auth->require_permission('can_manage_permissions');

		$current_user = $this->catalog_auth->user();
		$rows = $this->input->post('perm');
		if (!is_array($rows))
		{
			redirect('catalog/admin/employees');
			return;
		}

		foreach ($rows as $person_id => $payload)
		{
			$person_id = (int) $person_id;
			if ($person_id <= 0)
			{
				continue;
			}

			$this->catalog->upsert_permission($person_id, is_array($payload) ? $payload : array(), (int) $current_user['person_id']);
		}

		redirect('catalog/admin/employees');
	}

	public function save_catalog_settings()
	{
		$this->catalog_auth->require_permission('can_manage_catalog');

		$product_order = trim((string) $this->input->post('product_order'));
		$min_visible_stock = (int) $this->input->post('min_visible_stock');
		$thank_you_message = trim((string) $this->input->post('thank_you_message'));
		$catalog_announcement = trim((string) $this->input->post('catalog_announcement'));
		$disabled_link_message = trim((string) $this->input->post('disabled_link_message'));
		$checkout_terms = trim((string) $this->input->post('checkout_terms'));
		$catalog_price_tier_id = (int) $this->input->post('catalog_price_tier_id');
		$catalog_webhook_enabled = (int) $this->input->post('catalog_webhook_enabled') === 1 ? 1 : 0;
		$catalog_webhook_url = trim((string) $this->input->post('catalog_webhook_url'));
		$catalog_webhook_secret = trim((string) $this->input->post('catalog_webhook_secret'));
		$catalog_webhook_token = trim((string) $this->input->post('catalog_webhook_token'));
		$catalog_webhook_timeout = (int) $this->input->post('catalog_webhook_timeout');
		$catalog_webhook_connect_timeout = (int) $this->input->post('catalog_webhook_connect_timeout');
		$catalog_webhook_verify_ssl = (int) $this->input->post('catalog_webhook_verify_ssl') === 1 ? 1 : 0;
		$allowed_orders = array('name_asc', 'name_desc', 'newest', 'oldest');
		$available_tiers = $this->catalog->get_price_tiers();
		$allowed_tier_ids = array(0);
		foreach ($available_tiers as $tier)
		{
			$allowed_tier_ids[] = (int) $tier['id'];
		}
		if (!in_array($product_order, $allowed_orders, TRUE))
		{
			$product_order = 'name_asc';
		}
		if (!in_array($catalog_price_tier_id, $allowed_tier_ids, TRUE))
		{
			$catalog_price_tier_id = 0;
		}
		if ($min_visible_stock < 0)
		{
			$min_visible_stock = 0;
		}
		if ($thank_you_message === '')
		{
			$thank_you_message = 'Gracias por su pedido. Pronto nos comunicaremos con usted.';
		}
		if ($disabled_link_message === '')
		{
			$disabled_link_message = 'Este link fue desactivado por la empresa. Si necesitas ayuda, comunicate con nuestro equipo.';
		}
		if ($catalog_webhook_timeout < 1)
		{
			$catalog_webhook_timeout = 4;
		}
		if ($catalog_webhook_timeout > 30)
		{
			$catalog_webhook_timeout = 30;
		}
		if ($catalog_webhook_connect_timeout < 1)
		{
			$catalog_webhook_connect_timeout = 2;
		}
		if ($catalog_webhook_connect_timeout > 15)
		{
			$catalog_webhook_connect_timeout = 15;
		}
		if ($catalog_webhook_url !== '' && !preg_match('/^https?:\/\//i', $catalog_webhook_url))
		{
			$catalog_webhook_url = '';
			$this->session->set_flashdata('catalog_notice', 'URL de webhook invalida. Debe iniciar con http:// o https://');
		}

		$this->catalog->set_setting('product_order', $product_order);
		$this->catalog->set_setting('min_visible_stock', (string) $min_visible_stock);
		$this->catalog->set_setting('thank_you_message', $thank_you_message);
		$this->catalog->set_setting('catalog_announcement', $catalog_announcement);
		$this->catalog->set_setting('disabled_link_message', $disabled_link_message);
		$this->catalog->set_setting('checkout_terms', $checkout_terms);
		$this->catalog->set_setting('catalog_price_tier_id', (string) $catalog_price_tier_id);
		$this->catalog->set_setting('catalog_webhook_enabled', (string) $catalog_webhook_enabled);
		$this->catalog->set_setting('catalog_webhook_url', $catalog_webhook_url);
		$this->catalog->set_setting('catalog_webhook_secret', $catalog_webhook_secret);
		$this->catalog->set_setting('catalog_webhook_token', $catalog_webhook_token);
		$this->catalog->set_setting('catalog_webhook_timeout', (string) $catalog_webhook_timeout);
		$this->catalog->set_setting('catalog_webhook_connect_timeout', (string) $catalog_webhook_connect_timeout);
		$this->catalog->set_setting('catalog_webhook_verify_ssl', (string) $catalog_webhook_verify_ssl);
		$this->catalog->save_category_order($this->input->post('category_order'));

		if (!$this->session->flashdata('catalog_notice'))
		{
			$this->session->set_flashdata('catalog_notice', 'Configuracion de catalogo guardada.');
		}
		redirect('catalog/admin/settings');
	}

	public function create_template()
	{
		$this->catalog_auth->require_permission('can_manage_catalog');

		$name = trim((string) $this->input->post('name'));
		$primary_color = $this->normalize_color($this->input->post('primary_color'), '#d62828');
		$secondary_color = $this->normalize_color($this->input->post('secondary_color'), '#003049');
		$bg_color = $this->normalize_color($this->input->post('bg_color'), '#fcfaf5');
		$image_bg_color = $this->normalize_color($this->input->post('image_bg_color'), '#f4f1e6');
		$border_color = $this->normalize_color($this->input->post('border_color'), '#e4ddce');
		$text_color = $this->normalize_color($this->input->post('text_color'), '#0f172a');
		$muted_text_color = $this->normalize_color($this->input->post('muted_text_color'), '#546278');
		$title_size_px = $this->normalize_size($this->input->post('title_size_px'), 17, 12, 42);
		$desc_size_px = $this->normalize_size($this->input->post('desc_size_px'), 13, 10, 28);
		$price_size_px = $this->normalize_size($this->input->post('price_size_px'), 24, 14, 56);
		$logo_size_px = $this->normalize_size($this->input->post('logo_size_px'), 64, 36, 180);
		$card_style = trim((string) $this->input->post('card_style'));
		$set_active = (int) $this->input->post('set_active') === 1;

		if ($name === '')
		{
			$name = 'Plantilla ' . date('d/m/Y H:i');
		}

		if (!in_array($card_style, array('rounded', 'soft', 'sharp'), TRUE))
		{
			$card_style = 'rounded';
		}

		$template_id = $this->catalog->create_template(array(
			'name' => $name,
			'primary_color' => $primary_color,
			'secondary_color' => $secondary_color,
			'bg_color' => $bg_color,
			'image_bg_color' => $image_bg_color,
			'border_color' => $border_color,
			'text_color' => $text_color,
			'muted_text_color' => $muted_text_color,
			'title_size_px' => $title_size_px,
			'desc_size_px' => $desc_size_px,
			'price_size_px' => $price_size_px,
			'logo_size_px' => $logo_size_px,
			'card_style' => $card_style,
			'is_active' => 0,
			'created_at' => date('Y-m-d H:i:s')
		));

		if ($set_active)
		{
			$this->catalog->activate_template($template_id);
		}

		redirect('catalog/admin/templates');
	}

	public function activate_template($template_id)
	{
		$this->catalog_auth->require_permission('can_manage_catalog');
		$this->catalog->activate_template((int) $template_id);
		redirect('catalog/admin/templates');
	}

	public function update_template($template_id)
	{
		$this->catalog_auth->require_permission('can_manage_catalog');

		$template = $this->catalog->get_template((int) $template_id);
		if (!$template)
		{
			show_404();
			return;
		}

		$name = trim((string) $this->input->post('name'));
		$primary_color = $this->normalize_color($this->input->post('primary_color'), $template['primary_color']);
		$secondary_color = $this->normalize_color($this->input->post('secondary_color'), $template['secondary_color']);
		$bg_color = $this->normalize_color($this->input->post('bg_color'), $template['bg_color']);
		$image_bg_color = $this->normalize_color($this->input->post('image_bg_color'), $template['image_bg_color']);
		$border_color = $this->normalize_color($this->input->post('border_color'), $template['border_color']);
		$text_color = $this->normalize_color($this->input->post('text_color'), $template['text_color']);
		$muted_text_color = $this->normalize_color($this->input->post('muted_text_color'), $template['muted_text_color']);
		$title_size_px = $this->normalize_size($this->input->post('title_size_px'), (int) $template['title_size_px'], 12, 42);
		$desc_size_px = $this->normalize_size($this->input->post('desc_size_px'), (int) $template['desc_size_px'], 10, 28);
		$price_size_px = $this->normalize_size($this->input->post('price_size_px'), (int) $template['price_size_px'], 14, 56);
		$logo_size_px = $this->normalize_size($this->input->post('logo_size_px'), (int) $template['logo_size_px'], 36, 180);
		$card_style = trim((string) $this->input->post('card_style'));

		if ($name === '')
		{
			$name = $template['name'];
		}

		if (!in_array($card_style, array('rounded', 'soft', 'sharp'), TRUE))
		{
			$card_style = $template['card_style'];
		}

		$this->catalog->update_template((int) $template_id, array(
			'name' => $name,
			'primary_color' => $primary_color,
			'secondary_color' => $secondary_color,
			'bg_color' => $bg_color,
			'image_bg_color' => $image_bg_color,
			'border_color' => $border_color,
			'text_color' => $text_color,
			'muted_text_color' => $muted_text_color,
			'title_size_px' => $title_size_px,
			'desc_size_px' => $desc_size_px,
			'price_size_px' => $price_size_px,
			'logo_size_px' => $logo_size_px,
			'card_style' => $card_style
		));

		redirect('catalog/admin/templates');
	}

	public function delete_template($template_id)
	{
		$this->catalog_auth->require_permission('can_manage_catalog');
		$result = $this->catalog->delete_template((int) $template_id);

		if (!$result['ok'])
		{
			$this->session->set_flashdata('catalog_notice', $result['message']);
		}
		else
		{
			$this->session->set_flashdata('catalog_notice', 'Plantilla eliminada correctamente.');
		}

		redirect('catalog/admin/templates');
	}

	public function inventory_update_ecommerce($item_id)
	{
		$this->catalog_auth->require_permission('can_manage_inventory');
		$item_id = (int) $item_id;
		if ($item_id <= 0)
		{
			show_error('Item invalido.', 400);
			return;
		}

		$is_ecommerce = (int) $this->input->post('is_ecommerce') === 1 ? 1 : 0;
		$this->catalog->update_inventory_item_ecommerce($item_id, $is_ecommerce);
		$item = $this->catalog->get_inventory_item($item_id);
		if ($this->input->is_ajax_request())
		{
			$this->output
				->set_content_type('application/json')
				->set_output(json_encode(array(
					'ok' => TRUE,
					'message' => 'Estado ecommerce actualizado.',
					'item' => $item
				)));
			return;
		}
		$this->session->set_flashdata('catalog_notice', 'Estado ecommerce actualizado.');
		redirect('catalog/admin/inventory');
	}

	public function inventory_upload_image($item_id)
	{
		$this->catalog_auth->require_permission('can_manage_inventory');
		$is_ajax = $this->input->is_ajax_request();
		$item_id = (int) $item_id;
		if ($item_id <= 0)
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'Item invalido.')));
				return;
			}
			show_error('Item invalido.', 400);
			return;
		}

		if (empty($_FILES['inventory_image']) || !is_array($_FILES['inventory_image']))
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'Debes seleccionar una imagen.')));
				return;
			}
			$this->session->set_flashdata('catalog_notice', 'Debes seleccionar una imagen.');
			redirect('catalog/admin/inventory');
			return;
		}

		$file = $_FILES['inventory_image'];
		if (!isset($file['error']) || (int) $file['error'] !== UPLOAD_ERR_OK)
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'No se pudo subir la imagen.')));
				return;
			}
			$this->session->set_flashdata('catalog_notice', 'No se pudo subir la imagen.');
			redirect('catalog/admin/inventory');
			return;
		}

		if (empty($file['tmp_name']) || !is_uploaded_file($file['tmp_name']))
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'Archivo invalido.')));
				return;
			}
			$this->session->set_flashdata('catalog_notice', 'Archivo invalido.');
			redirect('catalog/admin/inventory');
			return;
		}

		$max_bytes = 5 * 1024 * 1024;
		if (!empty($file['size']) && (int) $file['size'] > $max_bytes)
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'La imagen supera 5MB.')));
				return;
			}
			$this->session->set_flashdata('catalog_notice', 'La imagen supera 5MB.');
			redirect('catalog/admin/inventory');
			return;
		}

		$original_name = isset($file['name']) ? (string) $file['name'] : 'image.jpg';
		$ext = strtolower(pathinfo($original_name, PATHINFO_EXTENSION));
		$allowed_ext = array('jpg', 'jpeg', 'png', 'gif', 'webp');
		if (!in_array($ext, $allowed_ext, TRUE))
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'Formato no permitido. Usa JPG, PNG, GIF o WEBP.')));
				return;
			}
			$this->session->set_flashdata('catalog_notice', 'Formato no permitido. Usa JPG, PNG, GIF o WEBP.');
			redirect('catalog/admin/inventory');
			return;
		}

		$binary = @file_get_contents($file['tmp_name']);
		if ($binary === FALSE || $binary === '')
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'No se pudo leer la imagen.')));
				return;
			}
			$this->session->set_flashdata('catalog_notice', 'No se pudo leer la imagen.');
			redirect('catalog/admin/inventory');
			return;
		}

		$safe_name = 'item_' . $item_id . '_' . date('Ymd_His') . '.' . $ext;
		$result = $this->catalog->add_inventory_item_image($item_id, $safe_name, $binary);
		if (!$result['ok'])
		{
			if ($this->input->is_ajax_request())
			{
				$this->output
					->set_status_header(400)
					->set_content_type('application/json')
					->set_output(json_encode(array(
						'ok' => FALSE,
						'message' => $result['message']
					)));
				return;
			}
			$this->session->set_flashdata('catalog_notice', $result['message']);
			redirect('catalog/admin/inventory');
			return;
		}

		$item = $this->catalog->get_inventory_item($item_id);
		if ($this->input->is_ajax_request())
		{
			$this->output
				->set_content_type('application/json')
				->set_output(json_encode(array(
					'ok' => TRUE,
					'message' => 'Imagen cargada correctamente.',
					'item' => $item
				)));
			return;
		}

		$this->session->set_flashdata('catalog_notice', 'Imagen cargada correctamente.');
		redirect('catalog/admin/inventory');
	}

	public function inventory_delete_image($item_id, $image_id)
	{
		$this->catalog_auth->require_permission('can_manage_inventory');
		$item_id = (int) $item_id;
		$image_id = (int) $image_id;
		$is_ajax = $this->input->is_ajax_request();
		if ($item_id <= 0 || $image_id <= 0)
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'Datos invalidos.')));
				return;
			}
			show_error('Datos invalidos.', 400);
			return;
		}

		$result = $this->catalog->delete_inventory_item_image($item_id, $image_id);
		if (!$result['ok'])
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => $result['message'])));
				return;
			}
			$this->session->set_flashdata('catalog_notice', $result['message']);
			redirect('catalog/admin/inventory');
			return;
		}

		$item = $this->catalog->get_inventory_item($item_id);
		if ($is_ajax)
		{
			$this->output
				->set_content_type('application/json')
				->set_output(json_encode(array(
					'ok' => TRUE,
					'message' => 'Imagen eliminada correctamente.',
					'item' => $item
				)));
			return;
		}

		$this->session->set_flashdata('catalog_notice', 'Imagen eliminada correctamente.');
		redirect('catalog/admin/inventory');
	}

	public function inventory_reorder_images($item_id)
	{
		$this->catalog_auth->require_permission('can_manage_inventory');
		$item_id = (int) $item_id;
		$is_ajax = $this->input->is_ajax_request();
		if ($item_id <= 0)
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => 'Item invalido.')));
				return;
			}
			show_error('Item invalido.', 400);
			return;
		}

		$image_ids = $this->input->post('image_ids');
		if (!is_array($image_ids))
		{
			$image_ids = array();
		}

		$result = $this->catalog->save_inventory_item_image_order($item_id, $image_ids);
		if (!$result['ok'])
		{
			if ($is_ajax)
			{
				$this->output->set_status_header(400)->set_content_type('application/json')->set_output(json_encode(array('ok' => FALSE, 'message' => $result['message'])));
				return;
			}
			$this->session->set_flashdata('catalog_notice', $result['message']);
			redirect('catalog/admin/inventory');
			return;
		}

		$item = $this->catalog->get_inventory_item($item_id);
		if ($is_ajax)
		{
			$this->output
				->set_content_type('application/json')
				->set_output(json_encode(array(
					'ok' => TRUE,
					'message' => 'Orden de fotos guardado.',
					'item' => $item
				)));
			return;
		}

		$this->session->set_flashdata('catalog_notice', 'Orden de fotos guardado.');
		redirect('catalog/admin/inventory');
	}

	private function csv_escape($value)
	{
		$value = str_replace('"', '""', (string) $value);
		return '"' . $value . '"';
	}

	private function render_admin($active_tab, $extra = array())
	{
		$permissions = $this->catalog_auth->permissions();
		$current_user = $this->catalog_auth->user();
		$links = $this->catalog->get_links(40);
		$orders = $this->catalog->get_recent_orders(60);
		$default_employee_id = (int) $this->catalog_config['catalog_default_employee_id'];
		if (is_array($current_user) && !empty($current_user['person_id']))
		{
			$default_employee_id = (int) $current_user['person_id'];
		}

		$data = array(
			'active_tab' => $active_tab,
			'catalog_name' => $this->catalog_config['catalog_name'],
			'current_user' => $current_user,
			'permissions' => $permissions,
			'customers' => $this->catalog->get_customers(),
			'employees' => $this->catalog->get_employees(),
			'locations' => $this->catalog->get_locations(),
			'employee_location_map' => $this->catalog->get_employee_locations_map(),
			'permission_rows' => $this->catalog->get_permissions_for_employees(),
			'links' => $links,
			'links_history' => $this->catalog->get_links_history(200),
			'orders' => $orders,
			'order_history' => $this->catalog->get_order_history(200),
			'inventory_items' => $this->catalog->get_inventory_items(250),
			'default_expire' => date('Y-m-d\TH:i', strtotime('+24 hours')),
			'currency_symbol' => $this->catalog_config['catalog_currency_symbol'],
			'token_bytes' => (int) $this->catalog_config['catalog_token_bytes'],
			'templates' => $this->catalog->get_templates(),
			'active_template' => $this->catalog->get_active_template(),
			'preview_url' => $this->resolve_preview_url($links),
			'product_order' => $this->catalog->get_setting('product_order', 'name_asc'),
			'min_visible_stock' => max(0, (int) $this->catalog->get_setting('min_visible_stock', '0')),
			'thank_you_message' => $this->catalog->get_setting('thank_you_message', 'Gracias por su pedido. Pronto nos comunicaremos con usted.'),
			'catalog_announcement' => $this->catalog->get_setting('catalog_announcement', ''),
			'disabled_link_message' => $this->catalog->get_setting('disabled_link_message', 'Este link fue desactivado por la empresa. Si necesitas ayuda, comunicate con nuestro equipo.'),
			'checkout_terms' => $this->catalog->get_setting('checkout_terms', ''),
			'catalog_price_tier_id' => (int) $this->catalog->get_setting('catalog_price_tier_id', '0'),
			'catalog_webhook_enabled' => (int) $this->catalog->get_setting('catalog_webhook_enabled', $this->config->item('catalog_webhook_enabled') ? '1' : '0'),
			'catalog_webhook_url' => $this->catalog->get_setting('catalog_webhook_url', (string) $this->config->item('catalog_webhook_url')),
			'catalog_webhook_secret' => $this->catalog->get_setting('catalog_webhook_secret', (string) $this->config->item('catalog_webhook_secret')),
			'catalog_webhook_token' => $this->catalog->get_setting('catalog_webhook_token', (string) $this->config->item('catalog_webhook_token')),
			'catalog_webhook_timeout' => max(1, (int) $this->catalog->get_setting('catalog_webhook_timeout', (string) (int) $this->config->item('catalog_webhook_timeout'))),
			'catalog_webhook_connect_timeout' => max(1, (int) $this->catalog->get_setting('catalog_webhook_connect_timeout', (string) (int) $this->config->item('catalog_webhook_connect_timeout'))),
			'catalog_webhook_verify_ssl' => (int) $this->catalog->get_setting('catalog_webhook_verify_ssl', $this->config->item('catalog_webhook_verify_ssl') ? '1' : '0'),
			'price_tiers' => $this->catalog->get_price_tiers(),
			'category_order_rows' => $this->catalog->get_category_order_rows((int) $this->catalog_config['catalog_default_location_id']),
			'default_employee_id' => $default_employee_id,
			'default_location_id' => (int) $this->catalog_config['catalog_default_location_id'],
			'stats' => array(
				'links_active' => $this->count_active_links($links),
				'orders_total' => count($orders),
				'sales_amount' => $this->sum_orders_total($orders)
			)
		);

		if (is_array($extra))
		{
			$data = array_merge($data, $extra);
		}

		$this->load->view('catalog/admin_dashboard', $data);
	}

	private function count_active_links($links)
	{
		$count = 0;
		foreach ($links as $row)
		{
			if (strtotime($row['expires_at']) >= time())
			{
				$count++;
			}
		}
		return $count;
	}

	private function sum_orders_total($orders)
	{
		$total = 0;
		foreach ($orders as $row)
		{
			$total += (float) $row['total'];
		}
		return $total;
	}

	private function normalize_color($raw, $fallback)
	{
		$raw = strtoupper(trim((string) $raw));
		if (preg_match('/^#[0-9A-F]{6}$/', $raw))
		{
			return $raw;
		}

		return $fallback;
	}

	private function normalize_size($raw, $fallback, $min, $max)
	{
		$value = (int) $raw;
		if ($value < (int) $min || $value > (int) $max)
		{
			return (int) $fallback;
		}

		return $value;
	}

	private function resolve_preview_url($links)
	{
		foreach ($links as $row)
		{
			if (strtotime($row['expires_at']) >= time())
			{
				return site_url('catalog/l/' . rawurlencode($row['token']));
			}
		}

		return NULL;
	}
}
