package dc

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
)

var publicConfigRe = regexp.MustCompile(`^\s*proxy_for\s+(\d+)\s+(\S+?)?;\s*$`)

type PublicConfigUpdater struct {
	updater

	http *http.Client
	tg   *Telegram
}

func (p *PublicConfigUpdater) Run(ctx context.Context, url, network string) {
	p.run(ctx, func() error {
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
		if err != nil {
			panic(err)
		}

		resp, err := p.http.Do(req)
		if err != nil {
			if resp != nil {
				io.Copy(io.Discard, resp.Body) //nolint: errcheck
				resp.Body.Close()              //nolint: errcheck
			}
			return fmt.Errorf("cannot fetch url %s: %w", url, err)
		}

		if resp.StatusCode >= http.StatusBadRequest {
			return fmt.Errorf("unexpected status code from %s: %d", url, resp.StatusCode)
		}

		scanner := bufio.NewScanner(resp.Body)
		addrs := map[int][]Addr{}

		for scanner.Scan() {
			matches := publicConfigRe.FindStringSubmatch(scanner.Text())
			if len(matches) != 3 {
				continue
			}

			dc, err := strconv.Atoi(matches[1])
			if err != nil {
				continue
			}

			switch dc {
			// this is a list of DC we currently support. Other are ignored.
			case 203: // CDN DC
				p.logger.Info(fmt.Sprintf("found %s address for DC %d", matches[2], dc))
				addrs[dc] = append(addrs[dc], Addr{
					Network: network,
					Address: matches[2],
				})
			}
		}

		if err := scanner.Err(); err != nil {
			return fmt.Errorf("cannot read response body from %s: %w", url, err)
		}

		p.tg.lock.Lock()
		defer p.tg.lock.Unlock()

		if network == "tcp4" {
			p.tg.view.publicConfigs.v4 = addrs
		} else {
			p.tg.view.publicConfigs.v6 = addrs
		}

		return nil
	})
}

func NewPublicConfigUpdater(tg *Telegram, logger Logger, client *http.Client) *PublicConfigUpdater {
	return &PublicConfigUpdater{
		updater: updater{
			logger: logger,
			period: PublicConfigUpdateEach,
		},
		http: client,
		tg:   tg,
	}
}
