package ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.xml.datatype.DatatypeFactory;

import jakarta.annotation.Resource;
import jakarta.jws.WebService;
import jakarta.xml.bind.JAXB;
import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.kernel.container.service.server.AbstractService;
import ru.bitel.bgbilling.modules.tv.common.bean.TvAccount;
import ru.bitel.bgbilling.modules.tv.common.service.TvAccountService;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.BillingException;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.BillingException_Exception;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.Price;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.PricingServiceWS;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.Service;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.Service.AdditionalParameters;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.Service.AdditionalParameters.Entry;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.ServiceOffering;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.ServiceSpecification;
import ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.TimeFrame;
import ru.bitel.bgbilling.modules.tv.server.TvUtils;
import ru.bitel.oss.systems.inventory.product.common.bean.ProductOffering;
import ru.bitel.oss.systems.inventory.product.common.bean.ProductOfferingActivationMode;
import ru.bitel.oss.systems.inventory.product.common.bean.ProductSpecActivationMode;
import ru.bitel.oss.systems.order.product.common.service.ProductOrderService;

@WebService(endpointInterface = "ru.bitel.bgbilling.modules.tv.dyn.cti.tve.ws.pricing.PricingServiceWS")
public class PricingServiceWSImpl
	extends AbstractService
	implements PricingServiceWS
{
	@Resource
	private TvAccountService tvAccountService;

	@Resource
	private ProductOrderService productOrderService;

	private BillingException_Exception newException( String message )
	{
		BillingException exception = new BillingException();
		exception.setMessage( message );

		return new BillingException_Exception( message, exception );
	}

	private BillingException_Exception newException( String message, Throwable cause )
	{
		BillingException exception = new BillingException();
		exception.setMessage( message );

		return new BillingException_Exception( message, exception, cause );
	}

	private TvAccount getTvAccount( String accountNumber, Date time )
		throws BillingException_Exception
	{
		try
		{

			TvAccount result = tvAccountService.tvAccountGetByLogin( accountNumber, time );
			if( result == null )
			{
				throw newException( "Аккаунт " + accountNumber + " не найден." );
			}

			return result;
		}
		catch( BGException ex )
		{
		    getLogger().error( ex.getMessage(), ex );

			throw newException( ex.getMessage() );
		}
	}

	public ServiceOffering findOfferingById( Long serviceOfferingId )
		throws BillingException_Exception
	{
	    getLogger().info( "findOfferingById" );
		return null;
	}

	/**
	 * Получение списка предложений по покупке услуги
	 */
	public List<ServiceOffering> queryOfferings( String accountNumber, Service service )
		throws BillingException_Exception
	{
	    getLogger().info( "queryOfferings" );
	    getLogger().info( service.getServiceSpecification().getId() );
	    getLogger().info( service.getServiceSpecification().getName() );
	    getLogger().info( service.getServiceSpecification().getType() );

		try
		{
			return queryOfferingsImpl( accountNumber, service );
		}
		catch( Exception e )
		{
			throw newException( e.getMessage(), e );
		}
	}

	private Service serviceClone( Service service )
	{
		Service result = new Service();

		result.setId( service.getId() );
		result.setName( service.getName() );
		result.setDescription( service.getDescription() );
		result.setResource( service.getResource() );
		result.setServiceSpecification( service.getServiceSpecification() );
		result.setAdditionalParameters( service.getAdditionalParameters() );
		result.setValidFor( service.getValidFor() );
		result.setPeriodical( service.isPeriodical() );

		return result;
	}

	/**
	 * Получение списка предложений по покупке услуги
	 * @param accountNumber
	 * @param _service
	 * @return
	 * @throws BGException
	 */
	private List<ServiceOffering> queryOfferingsImpl( String accountNumber, Service _service )
		throws Exception
	{
		JAXB.marshal( _service, System.out );

		final List<ServiceOffering> result = new ArrayList<ServiceOffering>();

		final Date time = new Date();
		final TvAccount tvAccount = tvAccountService.tvAccountGetByLogin( accountNumber, time );

		final ServiceSpecification serviceSpecification = _service.getServiceSpecification();

		serviceSpecification.setDescription( "ASdASD" );

		// TODO: оптимизация
		List<ProductOffering> productOfferingList = productOrderService.productOfferingList( moduleId, tvAccount.getContractId(),
																							 tvAccount.getId(), -1, new Date(), true, false );

		for( ProductOffering productOffering : productOfferingList )
		{
			if( !productOffering.getProductSpec().getIdentifier().equals( String.valueOf( serviceSpecification.getId() ) ) )
			{
				continue;
			}

			for( ProductOfferingActivationMode offeringActivationMode : productOffering.getActivationModeList() )
			{
				ServiceOffering ctiOffering = new ServiceOffering();
				ctiOffering.setAccountNumber( accountNumber );
				ctiOffering.setDate( DatatypeFactory.newInstance().newXMLGregorianCalendar( new GregorianCalendar() ) );
				ctiOffering.setId( (long)offeringActivationMode.getProductSpecActivationMode().getId() );
				ctiOffering.setDescription( "bbbb" );

				Service ctiService = serviceClone( _service );

				ctiService.setPeriodical( true );

				ctiOffering.setService( ctiService );

				final ProductSpecActivationMode activationMode = offeringActivationMode.getProductSpecActivationMode();

				Price ctiPrice = new Price();
				ctiPrice.setAccountNumber( accountNumber );
				ctiPrice.setDate( DatatypeFactory.newInstance().newXMLGregorianCalendar( new GregorianCalendar() ) );
				ctiPrice.setDescription( "dddD" );
				ctiPrice.setId( 101L );
				ctiPrice.setCurrency( "RUB" );
				//ctiPrice.setValue( offeringActivationMode.getPrice() );//.offeringActivationMode.getActivationPrice()
				//offeringActivationMode.getPrice()

				ctiOffering.setPrice( ctiPrice );

				// аренда фильма?
				//if( !productOffering.getProductSpec().isPeriodic() )

				getLogger().info( "offeringActivationMode " + offeringActivationMode.getPeriodMode() );
				getLogger().info( offeringActivationMode.getPeriodAmount() );
				getLogger().info( offeringActivationMode.getPrice());
				getLogger().info( offeringActivationMode.getActivationPrice());

				int rentTime;
				if( BigDecimal.ZERO.compareTo( offeringActivationMode.getPrice() ) != 0 || BigDecimal.ZERO.compareTo( offeringActivationMode.getActivationPrice() ) == 0 )
				{
					ctiPrice.setValue( offeringActivationMode.getPrice() );
					
					rentTime = (int)TvUtils.periodAmountToUnit( offeringActivationMode.getPeriodMode(), offeringActivationMode.getPeriodAmount(), TimeUnit.HOURS );
				}
				else
				{
					ctiPrice.setValue( offeringActivationMode.getActivationPrice() );
					
					getLogger().info( activationMode.getPeriodMode() );
					getLogger().info( activationMode.getPeriodAmount() );
					rentTime = (int)TvUtils.periodAmountToUnit( activationMode.getPeriodMode(), activationMode.getPeriodAmount(), TimeUnit.HOURS );
				}

				AdditionalParameters additionalParameters = new AdditionalParameters();
				Entry e = new Entry();
				e.setKey( "rentTime" );
				e.setValue( (short)rentTime );
				additionalParameters.getEntry().add( e );

				ctiService.setAdditionalParameters( additionalParameters );

				TimeFrame timeFrame = new TimeFrame();

				GregorianCalendar calendar = new GregorianCalendar();
				calendar.set( Calendar.HOUR_OF_DAY, -1 );

				timeFrame.setStartTime( DatatypeFactory.newInstance().newXMLGregorianCalendar( (GregorianCalendar)calendar.clone() ) );

				calendar.add( Calendar.HOUR_OF_DAY, 3 );

				timeFrame.setEndTime( DatatypeFactory.newInstance().newXMLGregorianCalendar( calendar ) );

				ctiService.setValidFor( timeFrame );

				ctiService.setDescription( "cccc" );
				ctiService.getServiceSpecification().setDescription( "aaaa" );

				JAXB.marshal( ctiOffering, System.out );
				result.add( ctiOffering );
			}

			break;
		}

		return result;
	}
}
