// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package upload

import (
	"os"
	"path/filepath"
	"strings"
)

// files to handle
type work struct {
	// absolute file names
	countfiles []string // count files to process
	readyfiles []string // old reports to upload
	// relative names
	uploaded map[string]bool // reports that have been uploaded
}

// find all the files that look like counter files or reports
// that need to be uploaded. (There may be unexpected leftover files
// and uploading is supposed to be idempotent.)
func (u *uploader) findWork() work {
	localdir, uploaddir := u.dir.LocalDir(), u.dir.UploadDir()
	var ans work
	fis, err := os.ReadDir(localdir)
	if err != nil {
		u.logger.Printf("Could not find work: failed to read local dir %s: %v", localdir, err)
		return ans
	}

	mode, asof := u.dir.Mode()
	u.logger.Printf("Finding work: mode %s asof %s", mode, asof)

	// count files end in .v1.count
	// reports end in .json. If they are not to be uploaded they
	// start with local.
	for _, fi := range fis {
		if strings.HasSuffix(fi.Name(), ".v1.count") {
			fname := filepath.Join(localdir, fi.Name())
			_, expiry, err := u.counterDateSpan(fname)
			switch {
			case err != nil:
				u.logger.Printf("Error reading expiry for count file %s: %v", fi.Name(), err)
			case expiry.After(u.startTime):
				u.logger.Printf("Skipping count file %s: still active", fi.Name())
			default:
				u.logger.Printf("Collecting count file %s", fi.Name())
				ans.countfiles = append(ans.countfiles, fname)
			}
		} else if strings.HasPrefix(fi.Name(), "local.") {
			// skip
		} else if strings.HasSuffix(fi.Name(), ".json") && mode == "on" {
			// Collect reports that are ready for upload.
			reportDate := u.uploadReportDate(fi.Name())
			if !asof.IsZero() && !reportDate.IsZero() {
				// If both the mode asof date and the report date are present, do the
				// right thing...
				//
				// (see https://github.com/golang/go/issues/63142#issuecomment-1734025130)
				if asof.Before(reportDate) {
					// Note: since this report was created after telemetry was enabled,
					// we can only assume that the process that created it checked that
					// the counter data contained therein was all from after the asof
					// date.
					//
					// TODO(rfindley): store the begin date in reports, so that we can
					// verify this assumption.
					u.logger.Printf("Uploadable: %s", fi.Name())
					ans.readyfiles = append(ans.readyfiles, filepath.Join(localdir, fi.Name()))
				}
			} else {
				// ...otherwise fall back on the old behavior of uploading all
				// unuploaded files.
				//
				// TODO(rfindley): invert this logic following more testing. We
				// should only upload if we know both the asof date and the report
				// date, and they are acceptable.
				u.logger.Printf("Uploadable (missing date): %s", fi.Name())
				ans.readyfiles = append(ans.readyfiles, filepath.Join(localdir, fi.Name()))
			}
		}
	}

	fis, err = os.ReadDir(uploaddir)
	if err != nil {
		os.MkdirAll(uploaddir, 0777)
		return ans
	}
	// There should be only one of these per day; maybe sometime
	// we'll want to clean the directory.
	ans.uploaded = make(map[string]bool)
	for _, fi := range fis {
		if strings.HasSuffix(fi.Name(), ".json") {
			u.logger.Printf("Already uploaded: %s", fi.Name())
			ans.uploaded[fi.Name()] = true
		}
	}
	return ans
}
