package services

import (
	"context"
	"solidarity-service/models"
	mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi"
	ma "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
	"google.golang.org/grpc"
	"google.golang.org/protobuf/types/known/structpb"
)

type MobilityAccountService struct {
	Client mobilityaccounts.MobilityAccountsClient
}

func NewMobilityAccountService(mobilityAccountsDial string) (MobilityAccountService, error) {
	mobilityAccountsConn, err := grpc.Dial(mobilityAccountsDial, grpc.WithInsecure())

	client := mobilityaccounts.NewMobilityAccountsClient(mobilityAccountsConn)
	if err != nil {
		return MobilityAccountService{}, err
	}

	return MobilityAccountService{
		Client: client,
	}, nil
}

func (s MobilityAccountService) Login(ctx context.Context, username, password, namespace string) (*models.Account, error) {
	resp, err := s.Client.Login(ctx, &mobilityaccounts.LoginRequest{
		Username:  username,
		Password:  password,
		Namespace: namespace,
	})
	if err != nil {
		return nil, err
	}

	account := resp.Account.ToStorageType()
	return s.ToAccountModel(account), nil
}

func (s MobilityAccountService) UpdateAccountData(ctx context.Context, id string, data map[string]any) error {
	d, err := structpb.NewStruct(data)
	if err != nil {
		return err
	}

	if _, err = s.Client.UpdateData(ctx, &mobilityaccounts.UpdateDataRequest{
		Account: &mobilityaccounts.Account{
			Id:   id,
			Data: d,
		},
	}); err != nil {
		return err
	}

	return nil
}

func (s MobilityAccountService) UpdatePassword(ctx context.Context, id string, password string) bool {
	_, err := s.Client.ChangePassword(ctx, &mobilityaccounts.ChangePasswordRequest{
		Id:       id,
		Password: password,
	})
	if err == nil {
		return true
	} else {
		return false
	}
}

func (s MobilityAccountService) GetAccountUsername(ctx context.Context, username string, namespace string) (*models.Account, error) {
	resp, err := s.Client.GetAccountUsername(ctx, &mobilityaccounts.GetAccountUsernameRequest{
		Username:  username,
		Namespace: namespace,
	})
	if err != nil {
		return nil, err
	}
	return s.ToAccountModel(resp.Account.ToStorageType()), nil
}

func (s MobilityAccountService) Register(ctx context.Context, username string, password string, email string, phone_number string, data map[string]any, namespace string) (*models.Account, error) {
	account := &ma.Account{
		Authentication: ma.AccountAuth{
			Local: ma.LocalAuth{
				Username:    username,
				Password:    password,
				Email:       email,
				PhoneNumber: phone_number,
			},
		},
		Namespace: namespace,
		Data:      data,
	}

	acc, err := mobilityaccounts.AccountFromStorageType(account)
	if err != nil {
		return nil, err
	}

	resp, err := s.Client.Register(ctx, &mobilityaccounts.RegisterRequest{
		Account: acc,
	})

	if err != nil {
		return nil, err
	}

	return s.ToAccountModel(resp.Account.ToStorageType()), nil
}
func (s MobilityAccountService) UpdatePhoneNumber(ctx context.Context, accountid string, phone_number string, verified bool, verification_code string) error {
	_, err := s.Client.UpdatePhoneNumber(
		ctx,
		&mobilityaccounts.UpdatePhoneNumberRequest{
			Id:               accountid,
			PhoneNumber:      phone_number,
			Verified:         verified,
			VerificationCode: verification_code,
		},
	)

	if err != nil {
		return err
	}

	return nil
}

func (s MobilityAccountService) SetAccountType(ctx context.Context, id string, accountType string) error {
	account, err := s.Client.GetAccount(ctx, &mobilityaccounts.GetAccountRequest{
		Id: id,
	})
	if err != nil {
		return err
	}
	data := make(map[string]interface{})
	data["type"] = accountType
	dataStruct := &structpb.Struct{
		Fields: make(map[string]*structpb.Value),
	}

	for key, value := range data {
		stringValue, ok := value.(string)
		if !ok {
			continue
		}

		dataStruct.Fields[key] = &structpb.Value{
			Kind: &structpb.Value_StringValue{
				StringValue: stringValue,
			},
		}
	}
	account.Account.Data = dataStruct
	_, err = s.Client.UpdateData(ctx, &mobilityaccounts.UpdateDataRequest{
		Account: account.Account,
	})
	if err != nil {
		return err
	}
	return nil

}

func (s MobilityAccountService) UpdateAccountBirthDate(ctx context.Context, id string, namespace string, birthdate string) error {
	account, err := s.Client.GetAccount(ctx, &mobilityaccounts.GetAccountRequest{
		Id: id,
	})
	data := make(map[string]interface{})
	data["birthdate"] = birthdate

	dataStruct := &structpb.Struct{
		Fields: make(map[string]*structpb.Value),
	}

	for key, value := range data {
		stringValue, ok := value.(string)
		if !ok {
			continue
		}

		dataStruct.Fields[key] = &structpb.Value{
			Kind: &structpb.Value_StringValue{
				StringValue: stringValue,
			},
		}
	}
	account.Account.Data = dataStruct
	_, err = s.Client.UpdateData(ctx, &mobilityaccounts.UpdateDataRequest{
		Account: account.Account,
	})
	if err != nil {
		return err
	}
	return nil
}

func (s MobilityAccountService) ToAccountModel(account ma.Account) *models.Account {
	first_name := account.Data["first_name"].(string)
	last_name := account.Data["last_name"].(string)
	birth_date, ok := account.Data["birthdate"].(string)
	if !ok {
		birth_date = ""
	}
	accountType, ok := account.Data["type"].(string)
	if !ok {
		accountType = ""
	}
	phone_number, ok := account.Data["phone_number"].(string)
	if !ok {
		phone_number = ""
	}
	email, ok := account.Data["email"].(string)
	if !ok {
		email = ""
	}
	return &models.Account{
		ID:        account.ID,
		FirstName: first_name,
		LastName:  last_name,
		Email:     email,
		BirthDate: birth_date,
		Type:      accountType,
		LocalCredentials: models.LocalCredentials{
			Email:               account.Authentication.Local.Email,
			EmailVerified:       account.Authentication.Local.EmailValidation.Validated,
			EmailValidationCode: account.Authentication.Local.EmailValidation.ValidationCode,
			PhoneNumber:         phone_number,
			PhoneNumberVerified: account.Authentication.Local.PhoneNumberValidation.Validated,
		},
	}
}
