HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux ip-172-26-0-120 6.17.0-1009-aws #9~24.04.2-Ubuntu SMP Fri Mar 6 23:50:29 UTC 2026 x86_64
User: ubuntu (1000)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: /var/www/html/api.aianced.com/app/Services/AiAnalysisService.php
<?php

namespace App\Services;

use OpenAI\Laravel\Facades\OpenAI;

class AiAnalysisService
{
    /**
     * Analyze an idea and return a structured summary.
     * Switch between 'openai' and 'simulation' via AIANCED_AI_PROVIDER in .env
     */
    public function analyze(string $ideaText): array
    {
        if (config('aianced.ai_provider') === 'openai') {
            return $this->analyzeWithOpenAI($ideaText);
        }

        return $this->simulate($ideaText);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Real OpenAI call — strict token limits to prevent abuse & control cost
    // ─────────────────────────────────────────────────────────────────────────

    private function analyzeWithOpenAI(string $ideaText): array
    {
        // Hard cap input to 500 chars — no need to send more to OpenAI
        $truncated = mb_substr(trim($ideaText), 0, 500);

        $response = OpenAI::chat()->create([
            'model'       => config('aianced.openai_model', 'gpt-4o-mini'),
            'messages'    => [
                ['role' => 'system', 'content' => $this->systemPrompt()],
                ['role' => 'user',   'content' => $truncated],
            ],
            // businessModel + praise add ~40 tokens; 360 gives safe headroom
            'max_tokens'  => 360,
            'temperature' => 0.3,  // Low = consistent, focused output
            'top_p'       => 0.9,
            // Force JSON output — prevents markdown wrapping or free-text
            'response_format' => ['type' => 'json_object'],
        ]);

        $content = $response->choices[0]->message->content ?? null;

        if (!$content) {
            throw new \RuntimeException('OpenAI returned an empty response.');
        }

        $parsed = json_decode($content, true);

        if (!$parsed || !isset($parsed['quality'])) {
            throw new \RuntimeException('OpenAI response was not in the expected format.');
        }

        $quality = in_array($parsed['quality'], ['valid', 'vague', 'spam']) ? $parsed['quality'] : 'valid';
        $name    = isset($parsed['name']) && is_string($parsed['name']) && $parsed['name'] !== 'null'
            ? mb_substr(strip_tags($parsed['name']), 0, 50)
            : null;

        // Spam — return immediately, no analysis needed
        if ($quality === 'spam') {
            return ['quality' => 'spam', 'name' => $name];
        }

        if (!isset($parsed['productType'], $parsed['targetUsers'], $parsed['features'])) {
            throw new \RuntimeException('OpenAI response was not in the expected format.');
        }

        // Sanitize — keep only what the frontend needs, limit feature count
        return [
            'quality'       => $quality,
            'name'          => $name,
            'productType'   => mb_substr(strip_tags($parsed['productType']), 0, 80),
            'targetUsers'   => mb_substr(strip_tags($parsed['targetUsers']), 0, 120),
            'features'      => array_slice(
                array_map(fn($f) => mb_substr(strip_tags((string) $f), 0, 100), $parsed['features']),
                0, 6
            ),
            'businessModel' => mb_substr(strip_tags($parsed['businessModel'] ?? ''), 0, 80),
            'praise'        => mb_substr(strip_tags($parsed['praise'] ?? ''), 0, 220),
        ];
    }

    // ─────────────────────────────────────────────────────────────────────────
    // System prompt — tight instruction to keep output small and structured
    // ─────────────────────────────────────────────────────────────────────────

    private function systemPrompt(): string
    {
        return <<<PROMPT
You are a software product analyst. Analyze the user's product idea and respond with ONLY a JSON object using exactly these keys:

{
  "quality": "valid | vague | spam",
  "name": "first name if user mentions their own name (e.g. 'I'm John', 'My name is Sarah'), otherwise null",
  "productType": "short category label (max 8 words)",
  "businessModel": "how this business operates or makes money — e.g. 'E-Commerce Retail', 'B2B SaaS', 'Content Distribution Service', 'On-Demand Service Aggregator', 'Creator Economy Platform' (max 6 words)",
  "targetUsers": "who will use this (max 15 words)",
  "features": ["feature 1", "feature 2", "feature 3", "feature 4", "feature 5"],
  "praise": "one sentence that genuinely validates this specific idea — reference the actual concept, not generic praise (max 30 words)"
}

Rules:
- Return ONLY valid JSON. No markdown, no explanation, no extra keys.
- quality must be exactly one of: valid, vague, spam
  - spam: gibberish, random characters, test input, offensive content, or clearly not a software/product idea
  - vague: has product intent but too generic or lacks meaningful details (e.g. "I want to make an app")
  - valid: clear product idea with identifiable domain and purpose
- If quality is "spam": set productType, businessModel, targetUsers, praise to empty strings and features to []
- If quality is "vague" or "valid": fill all other fields normally
- features must have exactly 4 to 6 items (skip if spam).
- Each feature is a short phrase, max 8 words.
- businessModel must describe the business nature/model, not repeat productType.
- praise must feel personal and reference the actual idea, not a generic compliment.
PROMPT;
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Simulation fallback — keyword-based for local dev / no API key
    // ─────────────────────────────────────────────────────────────────────────

    private function simulate(string $ideaText): array
    {
        $text = strtolower($ideaText);

        $quality = $this->classifyQuality($text);
        $name    = $this->extractName($ideaText);

        if ($quality === 'spam') {
            return ['quality' => 'spam', 'name' => $name];
        }

        [$productType, $businessModel, $targetUsers, $features, $praise] = match (true) {
            $this->matches($text, ['podcast', 'audio', 'episode', 'listen', 'host', 'rss', 'stream', 'media', 'video', 'youtube', 'content creator', 'creator'])
                => $this->podcast(),
            $this->matches($text, ['fitness', 'workout', 'gym', 'trainer', 'exercise', 'health', 'wellness', 'nutrition', 'diet', 'coach'])
                => $this->fitness(),
            $this->matches($text, ['job', 'hiring', 'recruit', 'talent', 'freelance', 'candidate', 'resume', 'cv', 'career'])
                => $this->jobs(),
            $this->matches($text, ['real estate', 'property', 'rent', 'apartment', 'house', 'tenant', 'landlord', 'mortgage'])
                => $this->realEstate(),
            $this->matches($text, ['fintech', 'finance', 'payment', 'wallet', 'banking', 'invoice', 'expense', 'accounting', 'budget'])
                => $this->fintech(),
            $this->matches($text, ['marketplace', 'sell', 'buy', 'listing', 'vendor'])
                => $this->marketplace(),
            $this->matches($text, ['booking', 'appointment', 'schedule', 'reservation', 'calendar'])
                => $this->booking(),
            $this->matches($text, ['delivery', 'food', 'restaurant', 'order', 'courier'])
                => $this->delivery(),
            $this->matches($text, ['learning', 'course', 'education', 'lms', 'training', 'tutor', 'school', 'quiz', 'lesson'])
                => $this->elearning(),
            $this->matches($text, ['saas', 'dashboard', 'analytics', 'report', 'data', 'automation', 'workflow', 'crm', 'erp', 'tool'])
                => $this->saas(),
            $this->matches($text, ['social', 'community', 'network', 'post', 'follow', 'feed', 'chat', 'forum', 'group'])
                => $this->social(),
            $this->matches($text, ['ecommerce', 'shop', 'store', 'cart', 'product', 'checkout', 'retail'])
                => $this->ecommerce(),
            default => $this->generic(),
        };

        return ['quality' => $quality, 'name' => $name, 'productType' => $productType, 'businessModel' => $businessModel, 'targetUsers' => $targetUsers, 'features' => $features, 'praise' => $praise];
    }

    private function classifyQuality(string $text): string
    {
        $trimmed = trim($text);

        // Spam: too short, or looks like gibberish / test input
        if (mb_strlen($trimmed) < 15) return 'spam';

        // Spam: mostly non-alphabetic (gibberish, random characters)
        $alphaCount = preg_match_all('/[a-z]/i', $trimmed);
        if ($alphaCount / mb_strlen($trimmed) < 0.4) return 'spam';

        // Spam: repeated characters (e.g. "hhhhhhhssssssss", "aaabbbccc")
        // Check: fewer than 5 unique characters in a string over 10 chars
        $uniqueChars = count(array_unique(str_split(strtolower(preg_replace('/\s+/', '', $trimmed)))));
        if ($uniqueChars < 5 && mb_strlen($trimmed) > 10) return 'spam';

        // Spam: single word with no spaces (likely gibberish if > 15 chars)
        $wordCount = str_word_count($trimmed);
        if ($wordCount <= 1 && mb_strlen($trimmed) > 15) return 'spam';

        // Spam: common test phrases
        $spamPhrases = ['test', 'hello', 'hi there', 'asdf', 'qwerty', 'lorem ipsum', 'aaa', 'bbb', '123'];
        foreach ($spamPhrases as $phrase) {
            if (trim($trimmed) === $phrase) return 'spam';
        }

        // Vague: has software/app intent but no real substance
        $vagueOnly = ['i want to make an app', 'i want to build an app', 'i have an idea', 'make a website', 'build a website', 'create an app', 'i want an app'];
        foreach ($vagueOnly as $phrase) {
            if (str_contains($trimmed, $phrase) && mb_strlen($trimmed) < 60) return 'vague';
        }

        // Vague: under 40 chars with product keywords but nothing else
        if (mb_strlen($trimmed) < 40) return 'vague';

        return 'valid';
    }

    private function extractName(string $ideaText): ?string
    {
        // Match patterns like "I'm John", "I am Sarah", "My name is Alex", "This is Mike"
        if (preg_match("/(?:i(?:'m| am)|my name is|this is)\s+([A-Z][a-z]{1,20})\b/i", $ideaText, $m)) {
            return mb_substr(strip_tags($m[1]), 0, 50);
        }
        return null;
    }

    private function matches(string $text, array $keywords): bool
    {
        foreach ($keywords as $kw) {
            if (str_contains($text, $kw)) return true;
        }
        return false;
    }

    private function podcast(): array     { return ['Podcast & Media Platform','Content Distribution Service','Content creators and listeners',['Podcast hosting & RSS feed','Episode player & playlists','Creator monetization tools','Listener subscriptions & tips','Analytics & audience insights'],'The creator economy is booming — a platform that helps podcasters publish, grow, and monetize their audience taps directly into this fast-growing market.']; }
    private function fitness(): array     { return ['Fitness & Wellness Platform','Creator Economy & Digital Coaching','Fitness enthusiasts and coaches',['Trainer profiles & programs','Workout video library','Progress & goal tracking','Live & on-demand classes','In-app payments & subscriptions'],'Health and fitness is one of the most loyal subscription categories — a platform connecting coaches with clients online has strong recurring revenue potential.']; }
    private function jobs(): array        { return ['Job & Talent Platform','Recruitment Marketplace','Job seekers and employers',['Job listings & search filters','Candidate profiles & resumes','AI-powered job matching','Application tracking system','Employer branding pages'],'Recruitment platforms that reduce time-to-hire for employers while giving candidates better visibility are consistently in high demand across every industry.']; }
    private function realEstate(): array  { return ['Real Estate Platform','Property Listing Marketplace','Buyers, renters, and agents',['Property listings with photos & maps','Advanced search & filters','Agent & landlord profiles','Virtual tour integration','Enquiry & scheduling system'],'Real estate is one of the highest-value transaction categories online — a focused platform for a specific market or region can build strong local dominance quickly.']; }
    private function fintech(): array     { return ['FinTech Application','B2B Financial Software','Businesses and financial teams',['Invoicing & payment collection','Expense tracking & budgeting','Multi-currency support','Financial reports & dashboards','Integrations with banks & accounting tools'],'FinTech tools that automate financial workflows save businesses significant time and money — this category consistently shows strong adoption and willingness to pay.']; }
    private function marketplace(): array { return ['Marketplace Platform','Two-Sided Marketplace','Buyers and sellers',['Seller profiles & storefronts','Product/service listings','Secure payment processing','Reviews and ratings','Messaging between users'],'A two-sided marketplace is one of the most proven digital business models — strong network effects make this increasingly valuable as it grows.']; }
    private function booking(): array     { return ['Booking & Scheduling Platform','Service Marketplace','Service providers and clients',['Service provider profiles','Real-time availability calendar','Online booking & confirmations','Automated reminders','Payments & cancellation policy'],'Scheduling and booking tools are in high demand across virtually every service industry — this is a practical, revenue-generating idea with clear user value.']; }
    private function delivery(): array    { return ['On-Demand Delivery Platform','On-Demand Service Aggregator','Customers, vendors, and drivers',['Vendor/restaurant listings','Real-time order tracking','Driver assignment engine','In-app payments','Ratings for drivers and vendors'],'On-demand delivery remains one of the fastest-growing sectors — a well-executed platform in this space can capture significant local market share quickly.']; }
    private function elearning(): array   { return ['E-Learning Platform','EdTech Subscription Service','Instructors and students',['Course creation & management','Video lessons & quizzes','Student progress tracking','Certificates of completion','Subscription or pay-per-course'],'The e-learning market is booming globally — a focused platform that serves a specific niche or skill set can build a loyal, recurring-revenue audience.']; }
    private function saas(): array        { return ['SaaS Dashboard Product','B2B Software-as-a-Service','Business teams and managers',['User authentication & roles','Data visualization & charts','Custom reports & exports','Team collaboration tools','Subscription billing & plans'],'B2B SaaS tools that save teams time or surface better decisions command strong subscription revenue — this idea sits in a category with proven willingness to pay.']; }
    private function social(): array      { return ['Social Community Platform','User-Generated Content Network','Content creators and communities',['User profiles & feeds','Posts, comments & reactions','Follow / connection system','Notifications & messaging','Content moderation tools'],'Community platforms built around a specific interest or identity tend to retain users far better than general social networks — a focused niche here is a real strength.']; }
    private function ecommerce(): array   { return ['E-Commerce Store','Direct-to-Consumer Retail','Online shoppers and store owners',['Product catalog & variants','Shopping cart & checkout','Payment gateway integration','Order management & tracking','Discount codes & promotions'],'E-commerce continues to grow year over year — a well-designed store with a strong niche or brand story can compete effectively even in a crowded market.']; }
    private function generic(): array     { return ['Custom Web Platform','Custom Digital Service','End users and administrators',['User registration & profiles','Core feature module','Admin dashboard','Notifications system','API-ready architecture'],'This is a well-scoped idea with a clear user need — the modular approach means you can launch lean, validate quickly, and build from a solid foundation.']; }
}