package text

import (
	"context"
	"fmt"
	"time"

	"github.com/rs/zerolog/log"
	"source.gleipnir.technology/Gleipnir/nidus-sync/comms/text"
	"source.gleipnir.technology/Gleipnir/nidus-sync/config"
	"source.gleipnir.technology/Gleipnir/nidus-sync/db"
	modelcomms "source.gleipnir.technology/Gleipnir/nidus-sync/db/gen/nidus-sync/comms/model"
	modelpublic "source.gleipnir.technology/Gleipnir/nidus-sync/db/gen/nidus-sync/public/model"
	modelpublicreport "source.gleipnir.technology/Gleipnir/nidus-sync/db/gen/nidus-sync/publicreport/model"
	querycomms "source.gleipnir.technology/Gleipnir/nidus-sync/db/query/comms"
	querypublic "source.gleipnir.technology/Gleipnir/nidus-sync/db/query/public"
	querypublicreport "source.gleipnir.technology/Gleipnir/nidus-sync/db/query/publicreport"
	"source.gleipnir.technology/Gleipnir/nidus-sync/lint"
	"source.gleipnir.technology/Gleipnir/nidus-sync/platform/background"
	"source.gleipnir.technology/Gleipnir/nidus-sync/platform/event"
	"source.gleipnir.technology/Gleipnir/nidus-sync/platform/types"
)

func ensureInitialText(ctx context.Context, txn db.Ex, dst types.E164) error {
	logs, err := querycomms.TextLogWelcomeFromDestination(ctx, txn, dst.PhoneString())
	if err != nil {
		return fmt.Errorf("Failed to query text logs: %w", err)
	}
	if len(logs) > 0 {
		return nil
	}
	return sendInitialText(ctx, txn, dst)
}
func resendInitialText(ctx context.Context, txn db.Ex, dst types.E164) error {
	phone, err := querycomms.ContactPhoneFromE164(ctx, txn, dst.PhoneString())
	if err != nil {
		return fmt.Errorf("Failed to find phone %s: %w", dst, err)
	}
	err = querycomms.PhoneUpdateStopMessageID(ctx, txn, phone.E164, nil)
	if err != nil {
		return fmt.Errorf("Failed to clear subscription on phone %s: %w", dst, err)
	}
	return nil
}
func sendInitialText(ctx context.Context, txn db.Ex, dst types.E164) error {
	content := "Welcome to Report Mosquitoes Online. We received your request and want to confirm text updates. Reply YES to continue. Reply STOP at any time to unsubscribe"
	_, err := sendTextDirect(ctx, txn, modelcomms.Textorigin_WebsiteAction, dst.PhoneString(), content, false, true)
	if err != nil {
		return fmt.Errorf("send text: %w", err)
	}
	return nil
}

// Begin the process of sending the text message, but only get as far as adding it to
// the database, then let the backend finish sending.
func sendTextBegin(ctx context.Context, txn db.Ex, user_id *int32, report_id *int32, destination types.E164, content string, type_ modelcomms.Textjobtype) (*int32, error) {
	job, err := querycomms.TextJobInsert(ctx, txn, modelcomms.TextJob{
		Content:     content,
		CreatorID:   user_id,
		Created:     time.Now(),
		Destination: destination.PhoneString(),
		//ID:
		ReportID: report_id,
		Source:   modelcomms.Textjobsource_Rmo,
		Type:     type_,
	})
	if err != nil {
		return nil, fmt.Errorf("Failed to add delayed text job: %w", err)
	}
	err = background.NewTextSend(ctx, txn, job.ID)
	if err != nil {
		return nil, fmt.Errorf("new background job: %w", err)
	}
	return &job.ID, nil
}
func sendTextCommandResponse(ctx context.Context, txn db.Ex, dst types.E164, content string) error {
	_, err := sendTextDirect(ctx, txn, modelcomms.Textorigin_CommandResponse, dst.PhoneString(), content, false, false)
	return err
}
func sendTextComplete(ctx context.Context, job modelcomms.TextJob) error {
	txn, err := db.BeginTxn(ctx)
	if err != nil {
		return fmt.Errorf("begin tx: %w", err)
	}
	defer lint.LogOnErrRollback(txn.Rollback, ctx, "rollback")
	dst, err := ParsePhoneNumber(job.Destination)
	if err != nil {
		return fmt.Errorf("parse phone: %w", err)
	}
	var origin modelcomms.Textorigin
	switch job.Type {
	case modelcomms.Textjobtype_ReportConfirmation:
		origin = modelcomms.Textorigin_WebsiteAction
	case modelcomms.Textjobtype_ReportMessage:
		origin = modelcomms.Textorigin_District
	default:
		return fmt.Errorf("incomplete switch: %s", string(job.Type))
	}
	destination, err := querycomms.PhoneFromE164(ctx, txn, dst.PhoneString())
	if err != nil {
		return fmt.Errorf("destination phone from e164: %w", err)
	}
	if destination.ConfirmedMessageID == nil {
		err := ensureInitialText(ctx, txn, *dst)
		if err != nil {
			return fmt.Errorf("Failed to ensure initial text has been sent: %w", err)
		}
		return nil
	}
	if destination.StopMessageID != nil {
		lint.LogOnErrCtx(func(ctx context.Context) error {
			return resendInitialText(ctx, txn, *dst)
		}, ctx, "resend initial text")
		return nil
	}
	text_log, err := sendTextDirect(ctx, txn, origin, job.Destination, job.Content, true, false)
	if err != nil {
		return fmt.Errorf("send text direct: %w", err)
	}
	err = querycomms.TextJobComplete(ctx, txn, int64(job.ID))
	if err != nil {
		return fmt.Errorf("update job: %w", err)
	}
	if job.ReportID != nil {
		creator_id := *job.CreatorID
		report_id := *job.ReportID
		log.Debug().Int32("creator", creator_id).Int32("report_id", report_id).Msg("Creating report entries for text message")
		querypublic.ReportTextInsert(ctx, txn, modelpublic.ReportText{
			CreatorID: creator_id,
			ReportID:  report_id,
			TextLogID: text_log.ID,
		})
		if err != nil {
			return fmt.Errorf("insert report_text: %w", err)
		}
		_, err = querypublicreport.ReportLogInsert(ctx, txn, modelpublicreport.ReportLog{
			Created:    time.Now(),
			EmailLogID: nil,
			// ID
			ReportID:  report_id,
			TextLogID: &text_log.ID,
			Type:      modelpublicreport.Reportlogtype_MessageText,
			UserID:    &creator_id,
		})
		if err != nil {
			return fmt.Errorf("insert report log: %w", err)
		}
		report, err := querypublicreport.ReportFromID(ctx, txn, int64(report_id))
		if err != nil {
			return fmt.Errorf("find public report: %w", err)
		}
		event.Updated(event.TypeRMOPublicReport, report.OrganizationID, report.PublicID)
	} else {
		log.Debug().Msg("no report info on text")
	}
	if err := txn.Commit(ctx); err != nil {
		return fmt.Errorf("commit: %w", err)
	}
	return nil
}

// Send a text message and save the appropriate database records.
// Send immediately using the current goroutine
func sendTextDirect(ctx context.Context, txn db.Ex, origin modelcomms.Textorigin, destination, content string, is_visible_to_llm, is_welcome bool) (modelcomms.TextLog, error) {
	text_log, err := querycomms.TextLogInsert(ctx, txn, modelcomms.TextLog{
		Content:        content,
		Created:        time.Now(),
		Destination:    destination,
		IsVisibleToLlm: is_visible_to_llm,
		IsWelcome:      is_welcome,
		Origin:         origin,
		Source:         config.PhoneNumberReportStr,
		TwilioSid:      nil,
		TwilioStatus:   "",
	})
	if err != nil {
		return modelcomms.TextLog{}, fmt.Errorf("insert text log: %w", err)
	}
	pid, err := text.SendText(ctx, config.VoipMSNumber, destination, content)
	if err != nil {
		return modelcomms.TextLog{}, fmt.Errorf("send text: %w", err)
	}
	err = querycomms.TextLogUpdate(ctx, txn, int64(text_log.ID), pid, "created")
	if err != nil {
		return modelcomms.TextLog{}, fmt.Errorf("update  %w", err)
	}
	return text_log, nil
}
