/**
 * Fundamentals Time Series module for retrieving detailed financial statement data over time.
 *
 * This module provides comprehensive time-series financial data including balance sheet,
 * income statement, and cash flow statement items across quarterly, annual, and trailing
 * periods. Essential for financial analysis, trend analysis, and fundamental research.
 *
 * @example Basic Usage - Balance Sheet Data
 * ```typescript
 * import YahooFinance from "yahoo-finance2";
 * const yahooFinance = new YahooFinance();
 *
 * // Get quarterly balance sheet data for the last 2 years
 * const balanceSheetData = await yahooFinance.fundamentalsTimeSeries('AAPL', {
 *   period1: '2022-01-01',
 *   period2: '2024-01-01',
 *   type: 'quarterly',
 *   module: 'balance-sheet'
 * });
 *
 * // Access specific metrics
 * balanceSheetData.forEach(quarter => {
 *   console.log(`${quarter.date}: Cash = $${quarter.quarterlyCashAndCashEquivalents}`);
 *   console.log(`Total Assets = $${quarter.quarterlyTotalAssets}`);
 * });
 * ```
 *
 * @example Financials (Income Statement) Analysis
 * ```typescript
 * // Get annual income statement data
 * const financialsData = await yahooFinance.fundamentalsTimeSeries('MSFT', {
 *   period1: '2020-01-01',
 *   type: 'annual',
 *   module: 'financials'
 * });
 *
 * // Track revenue growth over years
 * financialsData.forEach(year => {
 *   console.log(`${year.date.getFullYear()}: Revenue = $${year.annualTotalRevenue}`);
 *   console.log(`Net Income = $${year.annualNetIncome}`);
 * });
 *
 * // Calculate revenue growth rate
 * if (financialsData.length >= 2) {
 *   const latest = financialsData[financialsData.length - 1];
 *   const previous = financialsData[financialsData.length - 2];
 *   const growthRate = ((latest.annualTotalRevenue - previous.annualTotalRevenue) / previous.annualTotalRevenue) * 100;
 *   console.log(`Revenue growth: ${growthRate.toFixed(2)}%`);
 * }
 * ```
 *
 * @example Cash Flow Analysis
 * ```typescript
 * // Get cash flow data for operational analysis
 * const cashFlowData = await yahooFinance.fundamentalsTimeSeries('GOOGL', {
 *   period1: '2022-01-01',
 *   type: 'quarterly',
 *   module: 'cash-flow'
 * });
 *
 * // Analyze cash generation trends
 * cashFlowData.forEach(quarter => {
 *   const operatingCF = quarter.quarterlyOperatingCashFlow;
 *   const freeCF = quarter.quarterlyFreeCashFlow;
 *   const capex = quarter.quarterlyCapitalExpenditure;
 *
 *   console.log(`${quarter.date}: Operating CF = $${operatingCF}`);
 *   console.log(`Free CF = $${freeCF}, CapEx = $${capex}`);
 * });
 * ```
 *
 * @example Comprehensive Financial Analysis
 * ```typescript
 * // Get all financial data for complete analysis
 * const allData = await yahooFinance.fundamentalsTimeSeries('NVDA', {
 *   period1: '2023-01-01',
 *   type: 'quarterly',
 *   module: 'all'
 * });
 *
 * // Financial health analysis
 * allData.forEach(quarter => {
 *   const totalAssets = quarter.quarterlyTotalAssets;
 *   const totalDebt = quarter.quarterlyTotalDebt;
 *   const cash = quarter.quarterlyCashAndCashEquivalents;
 *   const revenue = quarter.quarterlyTotalRevenue;
 *
 *   // Key ratios
 *   const debtToAssets = (totalDebt / totalAssets) * 100;
 *   const cashRatio = (cash / totalDebt) * 100;
 *
 *   console.log(`${quarter.date}:`);
 *   console.log(`  Debt-to-Assets: ${debtToAssets.toFixed(2)}%`);
 *   console.log(`  Cash Coverage: ${cashRatio.toFixed(2)}%`);
 * });
 * ```
 *
 * @example Trailing Twelve Months (TTM) Data
 * ```typescript
 * // Get trailing 12-month data for current analysis
 * const ttmData = await yahooFinance.fundamentalsTimeSeries('TSLA', {
 *   period1: '2023-01-01',
 *   type: 'trailing',
 *   module: 'financials'
 * });
 *
 * // Get the most recent TTM figures
 * const latest = ttmData[ttmData.length - 1];
 * console.log(`TTM Revenue: $${latest.trailingTotalRevenue}`);
 * console.log(`TTM Net Income: $${latest.trailingNetIncome}`);
 * ```
 *
 * @example Comparative Analysis
 * ```typescript
 * // Compare multiple companies
 * const symbols = ['AAPL', 'MSFT', 'GOOGL'];
 * const comparisons = await Promise.all(
 *   symbols.map(symbol =>
 *     yahooFinance.fundamentalsTimeSeries(symbol, {
 *       period1: '2023-01-01',
 *       type: 'annual',
 *       module: 'financials'
 *     })
 *   )
 * );
 *
 * // Compare latest year revenues
 * symbols.forEach((symbol, index) => {
 *   const data = comparisons[index];
 *   const latestYear = data[data.length - 1];
 *   console.log(`${symbol}: $${latestYear.annualTotalRevenue} revenue`);
 * });
 * ```
 *
 * @example Error Handling and Data Validation
 * ```typescript
 * try {
 *   const data = await yahooFinance.fundamentalsTimeSeries('AAPL', {
 *     period1: '2020-01-01',
 *     type: 'quarterly',
 *     module: 'balance-sheet'
 *   });
 *
 *   // Check for data availability
 *   if (data.length === 0) {
 *     console.log('No financial data available for the specified period');
 *     return;
 *   }
 *
 *   // Validate data completeness
 *   data.forEach(quarter => {
 *     if (!quarter.quarterlyTotalAssets) {
 *       console.warn(`Missing total assets data for ${quarter.date}`);
 *     }
 *   });
 *
 * } catch (error) {
 *   console.error('Failed to fetch fundamentals data:', error.message);
 * }
 * ```
 *
 * @remarks
 * **Data Types**: The module supports three reporting periods:
 * - `quarterly`: Individual quarter reporting periods
 * - `annual`: Full fiscal year data
 * - `trailing`: Rolling 12-month (TTM) data
 *
 * **Module Types**: Financial statement categories:
 * - `financials`: Income statement items (revenue, expenses, net income)
 * - `balance-sheet`: Balance sheet items (assets, liabilities, equity)
 * - `cash-flow`: Cash flow statement items (operating, investing, financing)
 * - `all`: Complete financial data from all statements
 *
 * **Data Availability**: Historical depth varies by company and may be limited
 * for newer public companies. Large cap stocks typically have 5+ years of data.
 *
 * **Currency**: All values are in the company's reporting currency (usually USD
 * for US companies). Values are typically in absolute amounts, not per-share.
 *
 * **Timing**: Data is typically available 1-2 days after earnings release.
 * Trailing data is updated quarterly with the latest reported period.
 *
 * @see {@link quoteSummary} for current period fundamental data
 *
 * @module fundamentalsTimeSeries
 */
import Timeseries_Keys from "../lib/timeseries.js";
import { getTypedDefinitions } from "../lib/validate/index.js";
// @yf-schema: see the docs on how this file is automatically updated.
import schema from "./fundamentalsTimeSeries.schema.js";
const definitions = getTypedDefinitions(schema);
export const FundamentalsTimeSeries_Types = ["quarterly", "annual", "trailing"];
export const FundamentalsTimeSeries_Modules = [
    "financials",
    "balance-sheet",
    "cash-flow",
    "all",
];
const queryOptionsDefaults = {
    merge: false,
    padTimeSeries: true,
    lang: "en-US",
    region: "US",
    type: "quarterly",
};
export default function fundamentalsTimeSeries(symbol, queryOptionsOverrides, moduleOptions) {
    return this._moduleExec({
        moduleName: "options",
        query: {
            assertSymbol: symbol,
            url: `https://query1.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/${symbol}`,
            needsCrumb: false,
            definitions,
            schemaKey: "#/definitions/FundamentalsTimeSeriesOptions",
            defaults: queryOptionsDefaults,
            overrides: queryOptionsOverrides,
            transformWith: processQuery,
        },
        result: {
            definitions,
            schemaKey: "#/definitions/FundamentalsTimeSeriesResults",
            // deno-lint-ignore no-explicit-any
            transformWith(response) {
                if (!response || !response.timeseries) {
                    throw new Error(`Unexpected result: ${JSON.stringify(response)}`);
                }
                return processResponse(response);
            },
        },
        moduleOptions,
    });
}
/**
 * Transform the input options into query parameters.
 * The options module defines which keys that are used in the query.
 * The keys are joined together into the query parameter type and
 * pre-fixed with the options type (e.g. annualTotalRevenue).
 * @param queryOptions Input query options.
 * @returns Query parameters.
 */
const processQuery = function (queryOptions) {
    // Convert dates
    if (!queryOptions.period2)
        queryOptions.period2 = new Date();
    const dates = ["period1", "period2"];
    for (const fieldName of dates) {
        const value = queryOptions[fieldName];
        if (value instanceof Date) {
            queryOptions[fieldName] = Math.floor(value.getTime() / 1000);
        }
        else if (typeof value === "string") {
            const timestamp = new Date(value).getTime();
            if (isNaN(timestamp)) {
                throw new Error("yahooFinance.fundamentalsTimeSeries() option '" +
                    fieldName +
                    "' invalid date provided: '" +
                    value +
                    "'");
            }
            queryOptions[fieldName] = Math.floor(timestamp / 1000);
        }
    }
    // Validate query parameters.
    if (queryOptions.period1 === queryOptions.period2) {
        throw new Error("yahooFinance.fundamentalsTimeSeries() options `period1` and `period2` " +
            "cannot share the same value.");
    }
    else if (!FundamentalsTimeSeries_Types.includes(queryOptions.type || "")) {
        throw new Error("yahooFinance.fundamentalsTimeSeries() option type invalid.");
    }
    else if (!FundamentalsTimeSeries_Modules.includes(queryOptions.module || "")) {
        throw new Error("yahooFinance.fundamentalsTimeSeries() option module invalid.");
    }
    // Join the keys for the module into query types.
    const keys = Object.entries(Timeseries_Keys).reduce((previous, [module, keys]) => {
        if (queryOptions.module == "all") {
            return previous.concat(keys);
        }
        else if (module == queryOptions.module) {
            return previous.concat(keys);
        }
        else
            return previous;
    }, []);
    const queryType = queryOptions.type + keys.join(`,${queryOptions.type}`);
    return {
        period1: queryOptions.period1,
        period2: queryOptions.period2,
        type: queryType,
    };
};
const typicalFields = {
    BALANCE_SHEET: ["netDebt", "totalDebt", "currentAssets", "accountsPayable"],
    CASH_FLOW: ["operatingCashFlow", "changeInOtherWorkingCapital"],
    FINANCIALS: ["netIncome", "interestExpense"],
};
function entryType(entry) {
    const types = [];
    for (const [type, fields] of Object.entries(typicalFields)) {
        if (fields.some((field) => entry[field])) {
            types.push(type);
        }
    }
    if (types.length === 0) {
        console.log("Could not determine entry type:", entry);
        return "UNKNOWN";
    }
    if (types.length === 1) {
        return types[0];
    }
    return "ALL";
}
/**
 * Transforms the time-series into an array with reported values per period.
 * Each object represents a period and its properties are the data points.
 * Financial statement content variates and keys are skipped when empty.
 * The query keys include the option type  (e.g. annualTotalRevenue).
 * In the response the type is removed (e.g. totalRevenue) for
 * easier mapping by the client.
 * @param response Query response.
 * @returns Formatted response.
 */
// deno-lint-ignore no-explicit-any
const processResponse = function (response) {
    // deno-lint-ignore no-explicit-any
    const keyedByTimestamp = {};
    const replace = new RegExp(FundamentalsTimeSeries_Types.join("|"));
    for (let ct = 0; ct < response.timeseries.result.length; ct++) {
        let periodType = "UNKNOWN";
        const result = response.timeseries.result[ct];
        if (!result.timestamp || !result.timestamp.length) {
            continue;
        }
        for (let ct = 0; ct < result.timestamp.length; ct++) {
            const timestamp = result.timestamp[ct];
            const dataKey = Object.keys(result)[2];
            if (!keyedByTimestamp[timestamp]) {
                keyedByTimestamp[timestamp] = { date: timestamp };
            }
            if (!result[dataKey][ct] ||
                !result[dataKey][ct].reportedValue ||
                !result[dataKey][ct].reportedValue.raw) {
                continue;
            }
            const short = dataKey.replace(replace, "");
            const key = short == short.toUpperCase()
                ? short
                : short[0].toLowerCase() + short.slice(1);
            keyedByTimestamp[timestamp][key] = result[dataKey][ct].reportedValue.raw;
            const thisPeriodType = result[dataKey][ct].periodType;
            if (thisPeriodType) {
                if (periodType !== "UNKNOWN" && periodType !== thisPeriodType) {
                    throw new Error("periodType mismatch - please report " + periodType + " " +
                        thisPeriodType);
                }
                periodType = thisPeriodType;
                keyedByTimestamp[timestamp].periodType = periodType;
            }
            else {
                console.log("missing periodType", keyedByTimestamp[timestamp]);
            }
        }
    }
    return Object.values(keyedByTimestamp).map((entry) => ({
        TYPE: entryType(entry),
        ...entry,
    }));
};
