package mtproto

import (
	

	
	
	

	
)

// connect establishes connection using configured transport, creating
// new auth key if needed.
func ( *Conn) ( context.Context) ( error) {
	 := 
	if !.pfs {
		// Backward-compatible non-PFS behavior: dial timeout limits the whole
		// connect phase (dial + key exchange).
		var  context.CancelFunc
		,  = context.WithTimeout(, .dialTimeout)
		defer ()
	}

	 := 
	if .pfs {
		// Quote (PFS): "The generation of temporary and permanent auth keys can be done in parallel."
		// Link: https://core.telegram.org/api/pfs
		//
		// In PFS mode key generation can take longer, so we only apply dial timeout
		// to socket establishment.
		var  context.CancelFunc
		,  = context.WithTimeout(, .dialTimeout)
		defer ()
	}

	,  := .dialer()
	if  != nil {
		return errors.Wrap(, "dial failed")
	}
	.conn = 
	defer func() {
		if  != nil {
			multierr.AppendInto(&, .Close())
		}
	}()
	if .pfs {
		return .connectPFS()
	}

	 := .session()
	if .Key.Zero() {
		.log.Info(, "Generating new auth key")
		 := .clock.Now()
		if  := .createAuthKey();  != nil {
			return errors.Wrap(, "create auth key")
		}

		.log.Info(, "Auth key generated",
			log.Duration("duration", .clock.Now().Sub()),
		)
		return nil
	}

	.log.Info(, "Key already exists")
	if .ID == 0 {
		// NB: Telegram can return 404 error if session id is zero.
		//
		// See https://github.com/gotd/td/issues/107.
		.log.Debug(, "Generating new session id")
		if  := .newSessionID();  != nil {
			return 
		}
	}

	return nil
}

func ( *Conn) ( context.Context) error {
	if .permKey.Zero() {
		.log.Info(, "Generating new permanent auth key")
		 := .clock.Now()
		if  := .createPermAuthKey();  != nil {
			return errors.Wrap(, "create permanent auth key")
		}
		.log.Info(, "Permanent auth key generated",
			log.Duration("duration", .clock.Now().Sub()),
		)
	} else {
		// Reuse persisted permanent key to keep existing authorization.
		.log.Info(, "Permanent key already exists")
	}

	.log.Info(, "Generating new temporary auth key")
	 := .clock.Now()
	if  := .createTempAuthKey();  != nil {
		return errors.Wrap(, "create temporary auth key")
	}
	.log.Info(, "Temporary auth key generated",
		log.Duration("duration", .clock.Now().Sub()),
	)

	return nil
}

func ( *Conn) (
	 context.Context,
	 exchange.ExchangeMode,
	 int,
) (exchange.ClientExchangeResult, error) {
	 := exchange.NewExchanger(.conn, .dcID).
		WithClock(.clock).
		WithLogger(.log.Named("exchange").Logger()).
		WithTimeout(.exchangeTimeout).
		WithRand(.rand)
	if  == exchange.ExchangeModeTemporary {
		// Temporary mode maps to p_q_inner_data_temp_dc in exchange package.
		 = .WithTempMode()
	}
	return .Client(.rsaPublicKeys).Run()
}

func ( *Conn) ( context.Context) {
	if !.log.Enabled(, log.LevelDebug) {
		return
	}
	// Useful for debugging i/o timeout errors on tcp reads or writes.
	 := []log.Attr{
		log.Duration("timeout", .exchangeTimeout),
	}
	if ,  := .Deadline();  {
		 = append(, log.Time("context_deadline", ))
	}
	.log.Debug(, "Initializing new key exchange", ...)
}

// createAuthKey generates new authorization key.
func ( *Conn) ( context.Context) error {
	// Grab exclusive lock for writing.
	// It prevents message sending during key regeneration if server forgot current auth key.
	.exchangeLock.Lock()
	defer .exchangeLock.Unlock()

	.logExchangeInit()
	,  := .runExchange(, exchange.ExchangeModePermanent, 0)
	if  != nil {
		return 
	}

	.sessionMux.Lock()
	.authKey = .AuthKey
	.sessionID = .SessionID
	.salt = .ServerSalt
	.sessionMux.Unlock()

	return nil
}

func ( *Conn) ( context.Context) error {
	.exchangeLock.Lock()
	defer .exchangeLock.Unlock()

	.logExchangeInit()
	,  := .runExchange(, exchange.ExchangeModePermanent, 0)
	if  != nil {
		return 
	}

	.sessionMux.Lock()
	.permKey = .AuthKey
	// Creation timestamp is used by ENCRYPTED_MESSAGE_INVALID recovery policy.
	.permKeyCreatedAt = .clock.Now().Unix()
	.sessionMux.Unlock()

	return nil
}

func ( *Conn) ( context.Context) error {
	.exchangeLock.Lock()
	defer .exchangeLock.Unlock()

	.logExchangeInit()
	,  := .runExchange(, exchange.ExchangeModeTemporary, .tempKeyTTL)
	if  != nil {
		return 
	}

	 := .ExpiresAt
	if  == 0 {
		// Defensive fallback if exchange result does not expose expiry.
		 = .clock.Now().Unix() + int64(.tempKeyTTL)
	}

	.sessionMux.Lock()
	.authKey = .AuthKey
	.sessionID = .SessionID
	.salt = .ServerSalt
	.tempKeyExpiry = 
	.sessionMux.Unlock()

	return nil
}