import { AxiosInstance } from 'axios';

interface DivvyConfig {
    apiToken: string;
    env: 'prod' | 'staging';
    apiVersion: 'v1' | 'v2' | 'v3';
}

declare class DivvyBase {
    protected client: AxiosInstance;
    constructor(config: DivvyConfig);
    protected paginate<T>(endpoint: string, params: any): Promise<T[]>;
    /**
     * Generic function to stringify filter objects
     * @param filters - The filter object to stringify
     * @returns A string representation of the filters
     */
    protected stringifyFilters<T extends Record<string, any>>(filters: T): string;
    /**
     * Converts a value to a string, handling arrays and other types
     * @param value - The value to stringify
     * @returns The stringified value
     */
    private stringifyValue;
}

interface TransactionFilters {
    /**
     * Filter by card ID
     * @property {string} eq - Exact match for card ID
     * @property {string[]} in - Match any of the provided card IDs
     */
    cardId?: {
        eq?: string;
        in?: string[];
    };
    /**
     * Filter by merchant name
     * @property {string} eq - Exact match for merchant name
     */
    merchantName?: {
        eq: string;
    };
    /**
     * Filter by user ID
     * @property {string} eq - Exact match for user ID
     * @property {string[]} in - Match any of the provided user IDs
     */
    userId?: {
        eq?: string;
        in?: string[];
    };
    /**
     * Filter by budget ID
     * @property {string} eq - Exact match for budget ID
     * @property {string[]} in - Match any of the provided budget IDs
     */
    budgetId?: {
        eq?: string;
        in?: string[];
    };
    /**
     * Filter by transaction fees
     * @property {number} lte - Less than or equal to the specified amount
     * @property {number} gte - Greater than or equal to the specified amount
     */
    fees?: {
        lte?: number;
        gte?: number;
    };
    /**
     * Filter by transaction IDs
     * @property {string} eq - Exact match for transaction ID
     * @property {string[]} in - Match any of the provided transaction IDs
     */
    transactionIds?: {
        eq?: string;
        in?: string[];
    };
    /**
     * Filter by custom field IDs
     * @property {string} eq - Exact match for custom field ID
     * @property {string[]} in - Match any of the provided custom field IDs
     * @note This filter shows only the transactions with the specified custom fields.
     * It does not change which custom fields are shown for each transaction.
     */
    customFieldIds?: {
        eq?: string;
        in?: string[];
    };
    /**
     * Filter by custom field value IDs
     * @property {string} eq - Exact match for custom field value ID
     * @property {string[]} in - Match any of the provided custom field value IDs
     */
    customFieldValueIds?: {
        eq?: string;
        in?: string[];
    };
    /**
     * Filter by receipt status
     * @property {ReceiptStatus} eq - Exact match for receipt status
     * @property {ReceiptStatus} ne - Not equal to the specified receipt status
     * @property {ReceiptStatus[]} in - Match any of the provided receipt statuses
     * @property {ReceiptStatus[]} nin - Do not match any of the provided receipt statuses
     */
    receiptStatus?: {
        eq?: ReceiptStatus;
        ne?: ReceiptStatus;
        in?: ReceiptStatus[];
        nin?: ReceiptStatus[];
    };
    /**
     * Filter by transaction type
     * @property {TransactionType} eq - Exact match for transaction type
     * @property {TransactionType} ne - Not equal to the specified transaction type
     * @property {TransactionType[]} in - Match any of the provided transaction types
     * @property {TransactionType[]} nin - Do not match any of the provided transaction types
     */
    type?: {
        eq?: TransactionType;
        ne?: TransactionType;
        in?: TransactionType[];
        nin?: TransactionType[];
    };
    /**
     * Filter by transaction occurrence time
     * @property {string} lte - Less than or equal to the specified DateTime (ISO 8601 format)
     * @property {string} gte - Greater than or equal to the specified DateTime (ISO 8601 format)
     */
    occurredTime?: {
        lte?: string;
        gte?: string;
    };
    /**
     * Filter by locked status
     * @property {boolean} eq - Exact match for locked status
     */
    isLocked?: {
        eq: boolean;
    };
    /**
     * Filter by completion status
     * @property {boolean} eq - Exact match for completion status
     */
    complete?: {
        eq: boolean;
    };
    /**
     * Filter by transaction amount
     * @property {number} lte - Less than or equal to the specified amount
     * @property {number} gte - Greater than or equal to the specified amount
     */
    amount?: {
        lte?: number;
        gte?: number;
    };
    /**
     * Filter by review status
     * @property {boolean} eq - Exact match for review status
     */
    isReviewed?: {
        eq: boolean;
    };
}

interface ListTransactionsParams {
    max?: number;
    sort?: string;
    filters?: TransactionFilters;
    showCustomFieldIds?: string[];
}
type ReceiptStatus = 'VALIDATED' | 'NOT_VALIDATED' | 'ATTACHED' | 'MISSING' | 'NOT_REQUIRED' | 'NOT_ATTACHED';
type TransactionType = 'CLEAR' | 'DECLINE' | 'AUTHORIZATION' | 'OTHER';
interface Transaction {
    id: string;
    childTransactionIds: string[];
    isLocked: boolean;
    isReconciled: boolean;
    transactionType: TransactionType;
    parentTransactionId: string;
    userId: string;
    rawMerchantName: string;
    merchantName: string;
    budgetId: string;
    originalAuthTransactionId: string;
    isCredit: boolean;
    currencyData: {
        receiptRequired: boolean;
        reviewRequired: boolean;
    };
    occurredTime: Date;
    updatedTime: Date;
    authorizedTime: Date;
    complete: boolean;
    pointsAwarded: number;
    customFields: Array<{
        id: string;
        name: string;
        note: string;
        isRequired: boolean;
        selectedValues: Array<{
            id: string;
            value: string;
        }>;
    }>;
    network: 'VISA' | 'MASTERCARD';
    isParent: boolean;
    reviews: Array<{
        id: string;
        isApproved: boolean;
        note: string;
        createdTime: Date;
        deletedTime: Date;
        reviewerId: string;
    }>;
    amount: number;
    transactedAmount: number;
    fees: number;
    receiptStatus: ReceiptStatus;
    matchedClearTransactionId: string;
    accountingIntegrationTransactions: Array<{
        id: string;
        billable: boolean;
        integrationTxId: string;
        syncStatus: string;
        syncMessage: string;
        integrationType: string;
        integrationId: string;
        transactionRecordId: string;
        syncRequestId: string;
    }>;
    reviewers: Array<{
        approverType: 'ADMIN' | 'MANAGER' | 'NEXT_MANAGER' | 'BUDGET_OWNER' | 'BOOKKEEPER' | 'SPECIFIC_PERSON';
        reviewedTime: Date;
        reviewerId: number;
        status: 'WAITING' | 'APPROVED' | 'DENIED';
        userId: string;
    }>;
    cardId: string;
    receiptSyncStatus: 'NOT_SYNCED' | 'SYNCED' | 'SYNC_ERROR' | 'NO_ATTACHMENTS';
    merchantCategoryCode: string;
    declineReason?: string;
}

declare class Transactions extends DivvyBase {
    /**
     * List All Transactions (This method automatically paginates through the results)
     * @param params
     * @returns
     */
    list(params: ListTransactionsParams): Promise<Transaction[]>;
    /**
     *  Get a transaction by id
     * @param transactionId
     * @returns
     */
    get(transactionId: string): Promise<Transaction>;
}

interface CustomField {
    id: string;
    name: string;
    description: string;
    type: 'CUSTOM_SELECTOR' | 'NOTE';
    multiSelect: boolean;
    allowCustomValues: boolean;
    minimumAmountForRequirement: number;
    required: boolean;
    global: boolean;
}
/**
 * Interface for CustomField filters
 */
interface CustomFieldFilters {
    /**
     * Filter by custom field name
     * @property {string} eq - Exact match for the custom field name
     * @property {string} sw - Starts with the specified string
     */
    name?: {
        eq?: string;
        sw?: string;
    };
    /**
     * Filter by retired status
     * @property {boolean} eq - Exact match for retired status
     */
    retired?: {
        eq: boolean;
    };
}
interface ListCustomFieldsParams {
    sort?: string;
    max?: number;
    filters?: CustomFieldFilters;
}
interface CreateCustomField {
    name: string;
    description?: string;
    multiSelect?: boolean;
    allowCustomValues: boolean;
    minimumAmountForRequirement?: number;
    required?: boolean;
    global?: boolean;
    values?: string[];
    selectedBudgetIds?: string[];
    requiredBudgetIds?: string[];
}
interface UpdateCustomField {
    name?: string;
    description?: string;
    allowCustomValues?: boolean;
    minimumAmountForRequirement?: number;
    isRequired?: boolean;
    isGlobal?: boolean;
}
interface ListCustomFieldValuesParams {
    filters?: {
        name?: {
            eq?: string;
            sw?: string;
        };
    };
    max?: number;
}
interface CustomFieldValue {
    id: string;
    value: string;
    deleted: boolean;
}

declare class CustomFields extends DivvyBase {
    /**
     * List All Custom Fields (This method automatically paginates through the results)
     * @param params
     * @returns
     */
    list(params?: ListCustomFieldsParams): Promise<CustomField[]>;
    /**
     * Get a custom field by id
     * @param customFieldId
     * @returns
     */
    get(customFieldId: string): Promise<CustomField>;
    /**
     *  Create a custom field
     * @param data
     * @returns
     */
    create(data: CreateCustomField): Promise<CustomField>;
    /**
     * Update a custom field
     * @param customFieldId
     * @param data
     * @returns
     */
    update(customFieldId: string, data: UpdateCustomField): Promise<CustomField>;
    /**
     *  List values for a custom field  (This method automatically paginates through the results)
     * @param customFieldId
     * @param params
     * @returns
     */
    listValues(customFieldId: string, params: ListCustomFieldValuesParams): Promise<CustomFieldValue[]>;
    /**
     *  Get a value for a custom field
     * @param customFieldId
     * @param customFieldValueId
     * @returns
     */
    getValue(customFieldId: string, customFieldValueId: string): Promise<CustomFieldValue>;
    /**
     * Create values for a custom field
     * @param customFieldId
     * @param values
     */
    createValues(customFieldId: string, values: string[]): Promise<void>;
    /**
     *  Delete values from a custom field
     * @param customFieldId
     * @param customFieldValueIds -   Array of custom field value ids to delete
     */
    deleteValues(customFieldId: string, customFieldValueIds: string[]): Promise<void>;
}

type UserRole = 'ADMIN' | 'APPROVER' | 'AUDITOR' | 'BOOKKEEPER' | 'MEMBER' | 'NO_ACCESS';
interface User {
    id: string;
    firstName: string;
    lastName: string;
    email: string;
    receiptEmail: string;
    retired: boolean;
    useCompanyMailingAddress: boolean;
    smsOptIn: boolean;
    hasDateOfBirth: boolean;
    role: UserRole;
    createdTime: string;
}
interface ListUsersParams {
    max?: number;
    nextPage?: string;
    prevPage?: string;
    filters?: {
        retired?: {
            eq: boolean;
        };
    };
}
interface CreateUserParams {
    firstName: string;
    lastName: string;
    email: string;
    role: 'ADMIN' | 'AUDITOR' | 'BOOKKEEPER' | 'MEMBER';
    dateOfBirth?: string;
}

declare class Users extends DivvyBase {
    /**
     * List All Users (This method automatically paginates through the results)
     * @param params
     * @returns
     */
    list(params?: ListUsersParams): Promise<User[]>;
    /**
     * Create a new user
     * @param data
     * @returns
     */
    create(data: CreateUserParams): Promise<User>;
    /**
     * Get current user details
     * @returns
     */
    getCurrent(): Promise<User>;
    /**
     * Get user details by ID
     * @param userId
     * @returns
     */
    get(userId: string): Promise<User>;
    /**
     * Delete a user
     * @param userId
     */
    delete(userId: string): Promise<void>;
}

declare class Divvy {
    transactions: Transactions;
    customFields: CustomFields;
    users: Users;
    constructor(config: DivvyConfig);
}

export { type CreateCustomField, type CreateUserParams, type CustomField, type CustomFieldFilters, type CustomFieldValue, Divvy, type DivvyConfig, type ListCustomFieldValuesParams, type ListCustomFieldsParams, type ListTransactionsParams, type ListUsersParams, type ReceiptStatus, type Transaction, type TransactionFilters, type TransactionType, type UpdateCustomField, type User, type UserRole };
