package main

import (
	"fmt"
	"net"
	"regexp"
	"time"

	"code.justin.tv/release/trace/pbmsg"
)

type callWithClient struct {
	client *pbmsg.Service
	call   *pbmsg.Call
}

func (c callWithClient) networkTime() time.Duration {
	return pbmsg.NetworkDuration(c.call)
}

func (c callWithClient) clientTime() time.Duration {
	return pbmsg.ClientDuration(c.call)
}

func (c callWithClient) String() string {
	from, to := c.client.GetHost(), svcHost(c.call)
	return fmt.Sprintf("%s -> %s: %s (%s -> %s)", from, to, c.networkTime(), pbmsg.ClientDuration(c.call), pbmsg.ServiceDuration(c.call))
}

// get all calls in a transaction
func calls(tx *pbmsg.Transaction) []callWithClient {
	return allCalls(callWithClient{tx.GetClient(), tx.GetRoot()})
}

func allCalls(c callWithClient) []callWithClient {
	out := make([]callWithClient, 0)
	out = append(out, c)
	subcallClient := c.call.GetSvc()
	for _, sc := range c.call.GetSubcalls() {
		out = append(out, allCalls(callWithClient{client: subcallClient, call: sc})...)
	}
	return out
}

// get the service hostname for a call
func svcHost(c *pbmsg.Call) string {
	if svcHost := c.GetSvc().GetHost(); svcHost != "" {
		return svcHost
	}
	sentTo := c.GetRequestSentTo()
	if sentToHost, _, err := net.SplitHostPort(sentTo); err != nil {
		return sentToHost
	} else {
		return sentTo
	}
}

func isCrossDatacenter(c callWithClient) bool {
	var client, svc string
	if client = c.client.GetHost(); client == "" {
		return false
	}
	if svc = svcHost(c.call); svc == "" {
		return false
	}

	clientDC, svcDC := datacenterOfHost(client), datacenterOfHost(svc)
	if clientDC == "unknown" || svcDC == "unknown" {
		return false
	}
	return clientDC != svcDC
}

var hostRegexp = regexp.MustCompile(`\.([^\.]+)\.justin\.tv$`)

func datacenterOfHost(host string) string {
	match := hostRegexp.FindStringSubmatch(host)
	if len(match) == 0 {
		return "unknown"
	} else {
		return match[1]
	}
}
