<?php defined('SYSPATH') or die('No direct access allowed.');

/**
 * This class provides Admin Service functions
 *
 * @package    OSB
 * @subpackage Service
 * @category   Controllers/Admin
 * @author     Deon George
 * @copyright  (c) 2010 Open Source Billing
 * @license    http://dev.osbill.net/license.html
 */
class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
	protected $secure_actions = array(
		'autolist'=>FALSE, // @todo To Change
		'list'=>TRUE,
		'listbycheckout'=>TRUE,
		'listadslbilling'=>TRUE,
		'listadslservices'=>TRUE,
		'listdomainservices'=>TRUE,
		'listdomainservicesbysupplier'=>TRUE,
		'listhostservices'=>TRUE,
		'listhspaservices'=>TRUE,
		'listwebservices'=>TRUE,
		'listinvoicesoon'=>TRUE,
		'update'=>TRUE,
		'view'=>TRUE,
	);

	public function action_autolist() {
		$return = array();

		$s = ORM::factory('service')->where('active','=',1);
		if (isset($_REQUEST['aid']))
			$s = $s->where('account_id','=',$_REQUEST['aid']);

		// @todo This should limit the results so that users dont see other users services.
		foreach ($s->find_all() as $so)
			array_push($return,array(
				'value'=>$so->id,
				'text'=>sprintf('%s: %s',$so->id,$so->service_name()),
			));

		$this->auto_render = FALSE;
		$this->response->headers('Content-Type','application/json');
		$this->response->body(json_encode($return));
	}

	/**
	 * Show a list of services
	 */
	public function action_list() {
		Block::add(array(
			'title'=>_('Customer Services'),
			'body'=>Table::display(
				ORM::factory('service')->find_all(),
				25,
				array(
					'id'=>array('label'=>'ID','url'=>'user/service/view/'),
					'service_name()'=>array('label'=>'Details'),
					'recur_schedule'=>array('label'=>'Billing'),
					'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
					'active'=>array('label'=>'Active'),
					'account->accnum()'=>array('label'=>'Cust ID'),
					'account->name()'=>array('label'=>'Customer'),
				),
				array(
					'page'=>TRUE,
					'type'=>'select',
					'form'=>'user/service/view',
				)),
			));
	}

	/**
	 * List all services by their default checkout method
	 */
	public function action_listbycheckout() {
		$svs = array();
		// @todo This needs to be configurable
		$go = ORM::factory('group',array('name'=>'Personal'));

		foreach (ORM::factory('account')->where('status','=',1)->find_all() as $ao)
			if ($ao->has_any('group',array($go)))
				foreach ($ao->service->list_active() as $so)
					if (! $so->service_billing->checkout_plugin_id)
						array_push($svs,$so);

		if ($svs)
			Block::add(array(
				'title'=>'Services that should be auto-billed',
				'body'=>Table::display(
					$svs,
					25,
					array(
						'id'=>array('label'=>'ID','url'=>'user/service/view/'),
						'service_name()'=>array('label'=>'Details'),
						'recur_schedule'=>array('label'=>'Billing'),
						'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
						'active'=>array('label'=>'Active'),
						'account->accnum()'=>array('label'=>'Cust ID'),
						'account->name()'=>array('label'=>'Customer'),
						'date_next_invoice'=>array('label'=>'Next Invoice'),
						'account->invoices_due_total(NULL,TRUE)'=>array('label'=>'Due Invoices'),
					),
					array(
						'page'=>TRUE,
						'type'=>'select',
						'form'=>'user/service/view',
					)),
				));

		foreach (ORM::factory('checkout')->where('active','=',1)->find_all() as $co) {
			$svs = array();

			foreach ($co->account->find_all() as $ao)
				foreach ($ao->service->list_active() as $so)
					if ($so->service_billing->checkout_plugin_id == $co->id)
						array_push($svs,$so);

			if ($svs)
				Block::add(array(
					'title'=>$co->name,
					'body'=>Table::display(
						$svs,
						25,
						array(
							'id'=>array('label'=>'ID','url'=>'user/service/view/'),
							'service_name()'=>array('label'=>'Details'),
							'recur_schedule'=>array('label'=>'Billing'),
							'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
							'active'=>array('label'=>'Active'),
							'account->accnum()'=>array('label'=>'Cust ID'),
							'account->name()'=>array('label'=>'Customer'),
							'date_next_invoice'=>array('label'=>'Next Invoice'),
							'account->invoices_due_total(NULL,TRUE)'=>array('label'=>'Due Invoices'),
						),
						array(
							'page'=>TRUE,
							'type'=>'select',
							'form'=>'user/service/view',
						)),
					));
		}
	}

	private function consoltraffic($svs,$date) {
		$data = array();

		foreach ($svs as $so) {
			$c = array();
			foreach ($so->plugin()->get_traffic_data_monthly($date) as $metric => $ma) {
				foreach ($ma as $month => $traffic) {
					// Only count the service once, not for each metric.
					if (! isset($c[$month])) {
						if (isset($data['svs'][$month]))
							$data['svs'][$month] += 1;
						else
							$data['svs'][$month] = 1;

						$c[$month] = 1;
					}

					if (isset($data['data'][$metric][$month]))
						$data['data'][$metric][$month] += (int)$traffic;
					else
						$data['data'][$metric][$month] = (int)$traffic;
				}
			}
		}

		ksort($data['svs']);
		foreach ($data['data'] as $metric => $details)
			ksort($data['data'][$metric]);

		return $data;
	}

	public function action_listadslservices() {
		$svs = ORM::factory('service')->list_bylistgroup('ADSL');
		$data = $this->consoltraffic($svs,time());

		$google = GoogleChart::factory('vertical_bar');
		$google->title = sprintf('ADSL traffic as at %s',date('Y-m-d',strtotime('yesterday')));
		$google->series(array('title'=>array_keys($data['data']),'axis'=>'x','data'=>$data['data']));
		$google->series(array('title'=>'Services','axis'=>'r','data'=>array('Services'=>$data['svs'])));

		Block::add(array('body'=>$google));

		Block::add(array(
			'title'=>_('ADSL Services'),
			'body'=>Table::display(
				$svs,
				NULL,
				array(
					'id'=>array('label'=>'ID','url'=>'user/service/view/'),
					'name()'=>array('label'=>'Service'),
					'plugin()->ipaddress()'=>array('label'=>'IP Address'),
					'product->plugin()->speed'=>array('label'=>'Speed'),
					'product->plugin()->allowance()'=>array('label'=>'Allowance'),
					'plugin()->traffic_thismonth()'=>array('label'=>'This Month'),
					'plugin()->traffic_lastmonth()'=>array('label'=>'Last Month'),
					'recur_schedule'=>array('label'=>'Billing'),
					'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
					'account->accnum()'=>array('label'=>'Cust ID'),
					'account->name()'=>array('label'=>'Customer'),
					'date_next_invoice'=>array('label'=>'Next Invoice'),
					'product->display("active")'=>array('label'=>'Current'),
				),
				array(
					'type'=>'select',
					'form'=>'user/service/view',
				)),
			));
	}

	public function action_listhspaservices() {
		$svs = ORM::factory('service')->list_bylistgroup('HSPA');
		$data = $this->consoltraffic($svs,time());

		$google = GoogleChart::factory('vertical_bar');
		$google->title = sprintf('HSPA traffic as at %s',date('Y-m-d',strtotime('yesterday')));
		$google->series(array('title'=>array_keys($data['data']),'axis'=>'x','data'=>$data['data']));
		$google->series(array('title'=>'Services','axis'=>'r','data'=>array('Services'=>$data['svs'])));

		Block::add(array('body'=>$google));

		Block::add(array(
			'title'=>_('HSPA Services'),
			'body'=>Table::display(
				$svs,
				NULL,
				array(
					'id'=>array('label'=>'ID','url'=>'user/service/view/'),
					'name()'=>array('label'=>'Service'),
					'plugin()->ipaddress()'=>array('label'=>'IP Address'),
					'product->plugin()->speed'=>array('label'=>'Speed'),
					'product->plugin()->allowance()'=>array('label'=>'Allowance'),
					'plugin()->traffic_thismonth()'=>array('label'=>'This Month'),
					'plugin()->traffic_lastmonth()'=>array('label'=>'Last Month'),
					'recur_schedule'=>array('label'=>'Billing'),
					'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
					'account->accnum()'=>array('label'=>'Cust ID'),
					'account->name()'=>array('label'=>'Customer'),
					'date_next_invoice'=>array('label'=>'Next Invoice'),
				),
				array(
					'type'=>'select',
					'form'=>'user/service/view',
				)),
			));
	}

	public function action_listdomainservices() {
		$svs = ORM::factory('service')->list_bylistgroup('DOMAIN');
		Sort::MAsort($svs,'name()');

		Block::add(array(
			'title'=>_('Domain Names'),
			'body'=>Table::display(
				$svs,
				25,
				array(
					'id'=>array('label'=>'ID','url'=>'user/service/view/'),
					'service_name()'=>array('label'=>'Details'),
					'plugin()->display("domain_expire")'=>array('label'=>'Expire'),
					'recur_schedule'=>array('label'=>'Billing'),
					'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
					'account->accnum()'=>array('label'=>'Cust ID'),
					'account->name()'=>array('label'=>'Customer'),
					'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
				),
				array(
					'page'=>TRUE,
					'type'=>'select',
					'form'=>'user/service/view',
				)),
			));
	}

	public function action_listdomainservicesbysupplier() {
		$svs = ORM::factory('service')->list_bylistgroup('DOMAIN');
		Sort::MAsort($svs,'plugin()->domain_registrar_id,name()');

		$list = array();

		foreach ($svs as $so)
			$list[$so->plugin()->domain_registrar_id][] = $so;

		foreach (array_keys($list) as $sid)
			Block::add(array(
				'title'=>sprintf(_('Domain Names by Supplier %s'),$sid),
				'body'=>Table::display(
					$list[$sid],
					25,
					array(
						'id'=>array('label'=>'ID','url'=>'user/service/view/'),
						'service_name()'=>array('label'=>'Details'),
						'plugin()->display("domain_expire")'=>array('label'=>'Expire'),
						'recur_schedule'=>array('label'=>'Billing'),
						'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
						'account->accnum()'=>array('label'=>'Cust ID'),
						'account->name()'=>array('label'=>'Customer'),
						'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
					),
					array(
						'page'=>TRUE,
						'type'=>'select',
						'form'=>'user/service/view',
					)),
				));
	}

	public function action_listhostservices() {
		$svs = ORM::factory('service')->list_bylistgroup('HOST');
		Sort::MAsort($svs,'name()');

		Block::add(array(
			'title'=>_('Hosting Services'),
			'body'=>Table::display(
				$svs,
				25,
				array(
					'id'=>array('label'=>'ID','url'=>'user/service/view/'),
					'service_name()'=>array('label'=>'Details'),
					'plugin()->display("host_expire")'=>array('label'=>'Expire'),
					'recur_schedule'=>array('label'=>'Billing'),
					'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
					'account->accnum()'=>array('label'=>'Cust ID'),
					'account->name()'=>array('label'=>'Customer'),
					'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
				),
				array(
					'page'=>TRUE,
					'type'=>'select',
					'form'=>'user/service/view',
				)),
			));
	}

	public function action_listwebservices() {
		$svs = ORM::factory('service')->list_bylistgroup('WEB');
		Sort::MAsort($svs,'name()');

		Block::add(array(
			'title'=>_('SSL Services'),
			'body'=>Table::display(
				$svs,
				25,
				array(
					'id'=>array('label'=>'ID','url'=>'user/service/view/'),
					'service_name()'=>array('label'=>'Details'),
					'recur_schedule'=>array('label'=>'Billing'),
					'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
					'account->accnum()'=>array('label'=>'Cust ID'),
					'account->name()'=>array('label'=>'Customer'),
					'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
				),
				array(
					'page'=>TRUE,
					'type'=>'select',
					'form'=>'user/service/view',
				)),
			));
	}

	/**
	 * Reconcile billing for an ADSL supplier
	 *
	 * @todo this should really be in a different class, since adsl wont be part of the main app
	 */
	public function action_listadslbilling($id) {
		$aso = ORM::factory('adsl_supplier',$id);

		// Process upload
		// @todo This should be separated out by supplier in case each supplier has a different format
		if ($_FILES) {
			$files = Validation::factory($_FILES)
				->rule('csv','Upload::valid')
				->rule('csv','Upload::not_empty')
				->rule('csv','Upload::type',array(':value',array('csv')))
				->rule('csv','Upload::size',array(':value','10M'));

			if ($files->check())
				foreach ($files as $file)
					$csv = $this->process($file);
		}

		// @todo add a display if there are no items
		$i = $j = 0;
		$total = 0;
		$summary = '';
		$output = View::factory($this->viewpath().'/head');
		$output .= '<table class="box-left">';
		foreach ($aso->services(TRUE) as $so) {
			$po = $so->plugin()->product();

			// Reset our uploaded data
			$uploaded = array();
			$uploaded['excess'] = empty($csv[$so->plugin()->service_number]['excess']) ? 0 : $csv[$so->plugin()->service_number]['excess'];

			// If our uploaded file has some cost data.
			if (! empty($csv[$so->plugin()->service_number])) {
				$uploaded['amount'] =
					(empty($csv[$so->plugin()->service_number]['cost']) ? 0 : $csv[$so->plugin()->service_number]['cost']) +
					(empty($csv[$so->plugin()->service_number]['credit']) ? 0 : $csv[$so->plugin()->service_number]['credit']);

				// Record the the exception if the cost is not expected
				if (round($po->adsl_supplier_plan->base_cost+$po->adsl_supplier_plan->tax(),2) != $uploaded['amount']) {
					$summary .= View::factory($this->viewpath().'/summary')
						->set('service',$so)
						->set('plan',$po)
						->set('planoverride',$so->plugin()->provided_adsl_plan_id ? TRUE : FALSE)
						->set('amount',$uploaded['amount'])
						->set('i',$j++%2);

					$uploaded['checked'] = '';
				} else {
					$uploaded['checked'] = 'checked="checked"';
				}

				unset($csv[$so->plugin()->service_number]);

			} else {
				$uploaded['checked'] = '';
				$uploaded['amount'] = 0;
			}

			$total += $uploaded['amount'];

			$output .= View::factory($this->viewpath().'/body')
				->set('service',$so)
				->set('plan',$po)
				->set('planoverride',$so->plugin()->provided_adsl_plan_id ? TRUE : FALSE)
				->set('checked',$uploaded['checked'])
				->set('amount',$uploaded['amount'])
				->set('excess',$uploaded['excess'])
				->set('adsl',$so->plugin())
				->set('i',$i++%2);
		}

		$output .= View::factory($this->viewpath().'/foot')
			->set('total',$total);

		$output .= '</table>';

		// Summary Report of remaining CSV items.
		if (! empty($csv))
			foreach ($csv as $service => $item) {
				$summary .= View::factory($this->viewpath().'/summary_exception')
					->set('service',$service)
					->set('item',$item)
					->set('i',$j++%2);
			}

		$output .= Form::open(NULL,array('enctype'=>'multipart/form-data'));
		$output .= '<div>';
		$output .= Form::file('csv');
		$output .= Form::submit('submit','upload',array('class'=>'form_button'));
		$output .= '</div>';
		$output .= Form::close();

		Block::add(array(
			'title'=>_('ADSL Services'),
			'body'=>$output,
		));

		if ($summary)
			Block::add(array(
				'title'=>_('Exception Charges'),
				'body'=>'<table class="box-left">'.$summary.'</table>',
				));

		Style::add(array(
			'type'=>'file',
			'data'=>'css/list.css',
		));
	}

	private function process(array $file) {
		$data = file_get_contents($file['tmp_name']);

		if (! $data)
			return;

		$start = $end = FALSE;
		$return = array();
		foreach (preg_split("/\n/",$data) as $line) {
			// Items start after "Item ID"
			if (! $start && preg_match('/^Item ID,/',$line)) {
				$start = true;
				continue;
			// Items end after "Subtotal"
			} elseif ($start && ! $end && preg_match('/^Subtotal:,/',$line)) {
				$end = true;
				continue;
			// If we havent started or not ended, continue
			} elseif (! $start || $end) {
				continue;
			}

			list($id,$ref,$unknown,$linedata,$q,$cost,$total) = explode(',',$line);

			// Extract the phone number from the $linedata
			@list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$linedata)));

			// If the description says Monthly Charge, we know its the monthly fee.
			if (preg_match('/^Monthly Charge/',$description))
				$return[$service]['cost'] = preg_replace('/\$/','',$total);
			// If the description says VISP credit, we know this is commission.
			elseif (preg_match('/^VISP Credit/',$description))
				$return[$service]['credit'] = preg_replace('/\$/','',$total);
			// If the description says Excess, we know this is excess charges.
			elseif (preg_match('/^Excess usage/',$description))
				$return[$service]['excess'] = preg_replace('/\$/','',$total);
			else
				$return[$service]['info'] = $line;
		}

		return $return;
	}

	/**
	 * List services that need to be invoiced.
	 */
	public function action_listinvoicesoon() {
		Block::add(array(
			'title'=>_('Services to Invoice'),
			'body'=>Table::display(
				ORM::factory('service')->list_invoicesoon(ORM::factory('invoice')->config('GEN_SOON_DAYS')),
				25,
				array(
					'id'=>array('label'=>'ID','url'=>'user/service/view/'),
					'service_name()'=>array('label'=>'Details'),
					'recur_schedule'=>array('label'=>'Billing'),
					'date_next_invoice'=>array('label'=>'Next Invoice'),
					'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
					'charges_new()'=>array('label'=>'Charges','class'=>'right'),
					'active'=>array('label'=>'Active'),
					'account->accnum()'=>array('label'=>'Cust ID'),
					'account->name()'=>array('label'=>'Customer'),
				),
				array(
					'page'=>TRUE,
					'type'=>'select',
					'form'=>'user/service/view',
				)),
			));
	}

	public function action_update() {
		$id = $this->request->param('id');
		$so = ORM::factory('service',$id);

		if (! $so->loaded())
			Request::current()->redirect('welcome/index');

		if ($_POST) {
			if (isset($_POST['plugin']) AND $_POST['plugin'])
				if (! $so->plugin()->values($_POST['plugin'])->update()->saved())
					throw new Kohana_Exception('Failed to save updates to plugin data for record :record',array(':record'=>$so->id()));

			if (! $so->values($_POST)->update()->saved())
				throw new Kohana_Exception('Failed to save updates to service data for record :record',array(':record'=>$so->id()));
		}

		Block::add(array(
			'title'=>sprintf('%s %s:%s',_('Update Service'),$so->id(),$so->name()),
			'body'=>View::factory($this->viewpath())
				->set('so',$so)
				->set('mediapath',Route::get('default/media'))
				->set('plugin_form',$so->admin_update()),
		));

		// @todo Investigate a better way of preparing for jscalendar
		Script::add(array(
			'type'=>'file',
			'data'=>'js/dhtml.calendar.js',
		));
		Script::add(array(
			'type'=>'file',
			'data'=>'js/dhtml.calendar-setup.js',
		));
		Script::add(array(
			'type'=>'file',
			'data'=>'js/dhtml.calendar-en.js',
		));
		Script::add(array(
			'type'=>'file',
			'data'=>'js/dhtml.date_selector.js',
		));
		Style::add(array(
			'type'=>'file',
			'data'=>'css/dhtml.calendar.css',
		));
	}

	public function action_view() {
		list($id,$output) = Table::page(__METHOD__);

		$so = ORM::factory('service',$id);

		if (! $so->loaded() OR ! Auth::instance()->authorised($so->account_id,$so->affiliate_id)) {
			$this->template->content = 'Unauthorised or doesnt exist?';
			return FALSE;
		}

		$doutput = $loutput = '';

		$loutput .= View::factory($this->viewpath())
			->set('so',$so);

		// Validate the transactions
		$bt = NULL;
		$save = (isset($_REQUEST['go']) && $_REQUEST['go']=1) ? 1 : 0;
		$xsid=197;
		foreach ($so->transactions()->where('item_type','=',0)->find_all() as $iio) {
			if ($iio->service_id == $xsid AND $iio->invoice_id < 3070) continue;
			if ($iio->quantity < 0 OR $iio->price_base < 0)
				continue;
			if (in_array($iio->id,array(960)))
				continue;

			if ($iio->invoice_id > 4000 OR $iio->product->prod_plugin_file=="ADSL")
				$a = FALSE;
			else
				$a = TRUE;

			if (is_null($bt))
				$bt = $iio->date_start;

//			$doutput .= sprintf('%s: BT is: %s [%s]<br/>',$iio->id,Config::date($bt),$bt);

			$pdata = Period::details($iio->recurring_schedule,$a ? NULL : $iio->product->price_recurr_weekday,$bt,TRUE);

			switch ($iio->recurring_schedule) {
				case 1:
				case 2:
				case 4:
				case 5:
					if ($iio->date_start != $pdata['start_time']) {
						$doutput .= sprintf('%s: Set start_time: %s [%s]<br/>',$iio->id,Config::date($pdata['start_time']),$pdata['start_time']);
						$iio->date_start=$pdata['start_time'];
					}

					if ($iio->date_stop != $pdata['end_time']) {
						$doutput .= sprintf('%s: Set end_time: %s [%s] <br/>',$iio->id,Config::date($pdata['end_time']),$pdata['end_time']);
						$iio->date_stop=$pdata['end_time'];
					}
					$bt = $pdata['end_time']+86400;

					//$doutput .= sprintf('%s: BT now: %s (%s) [%s]<br/>',$iio->id,Config::date($bt),Config::date($pdata['end_time']),$bt);
					break;

				default:
					$doutput .= sprintf('%s: %s Not handled',$iio->id,$iio->recurring_schedule);
			}
			//$doutput .= '<br/>';
			if ($save) {
				$iio->save();
			}
		}

		if (isset($_REQUEST['go']))
			$this->request->redirect('admin/service/view/'.$so->id);
		Block::add(array(
			'title'=>sprintf('Transaction History for %s: %s',$so->id(),$so->name()),
			'body'=>$loutput,
		));

		Block::add(array(
			'title'=>sprintf('Transaction Debug for %s: %s',$so->id(),$so->name()),
			'body'=>$doutput,
		));

		$output .= View::factory('service/user/view')
			->set('so',$so);

		Block::add(array(
			'title'=>sprintf('%s: %s',$so->id(),$so->service_name()),
			'body'=>$output,
			));

	}
}
?>
